From 97855f27c2b76a6ab134e6429bec988bf24e30ed Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 30 Oct 2019 18:31:00 +0100 Subject: [PATCH 01/38] WIP - CSharpClient --- .../csharp/NiryoOneClient/NiryoOneClient.cs | 34 +++++++++++++++++++ .../NiryoOneClient/NiryoOneClient.csproj | 7 ++++ 2 files changed, 41 insertions(+) create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs new file mode 100644 index 00000000..ad9d6990 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace NiryoOneClient +{ + public class NiryoOneClient + { + private TcpClient _client; + private int _port; + private string _server; + + public NiryoOneClient(string server, int port) + { + _server = server; + _port = port; + } + + public void Connect() + { + if (_client != null) + { + _client.Close(); + _client.Dispose(); + } + + _client = new TcpClient(_server, _port); + } + + + } +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj new file mode 100644 index 00000000..d4c395e8 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.1 + + + From deb8759795adbaef95a6fddb3addac0c64084537 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Thu, 31 Oct 2019 11:40:15 +0100 Subject: [PATCH 02/38] WIP --- .../csharp/NiryoOneClient/NiryoOneClient.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index ad9d6990..042901e0 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -1,11 +1,20 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices.ComTypes; using System.Text; +using System.Threading.Tasks; namespace NiryoOneClient { + public enum CalibrateMode + { + AUTO, + MANUAL + } + public class NiryoOneClient { private TcpClient _client; @@ -29,6 +38,59 @@ public void Connect() _client = new TcpClient(_server, _port); } + private string ToSnakeCaseUpper(string s) + { + return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); + } + + public async Task Calibrate(CalibrateMode mode) + { + await SendCommandAsync(nameof(Calibrate), mode.ToString()); + await ReceiveAnswerAsync(nameof(Calibrate)); + } + + public async Task SetLearningMode(bool mode) + { + await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); + await ReceiveAnswerAsync(nameof(SetLearningMode)); + } + + public async Task SendCommandAsync(string command_type, params string[] args) + { + if (!_client.Connected) + throw new SocketException(); + + string cmd; + if (args.Any()) + cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; + else + cmd = ToSnakeCaseUpper(command_type); + + await _client.GetStream().WriteAsync(Encoding.ASCII.GetBytes(cmd + "\n")); + } + + public async Task ReceiveAnswerAsync(string command_type) + { + var sr = new StreamReader(_client.GetStream()); + var result = await sr.ReadLineAsync(); + result = result.TrimEnd('\n'); + var colonSplit = result.Split(':', 2); + var cmd = colonSplit[0]; + if (cmd != ToSnakeCaseUpper(command_type)) + throw new NiryoOneException("Wrong command response received."); + var commaSplit2 = colonSplit[1].Split(':', 2); + var status = commaSplit2[0]; + if (status == "KO") + throw new NiryoOneException(commaSplit2[1]); + return commaSplit2[1].Split(','); + } + } + + public class NiryoOneException : Exception + { + public NiryoOneException(string reason) + { + } } } From 143ace7e2986652775ac11a2084a479f28bda877 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Thu, 31 Oct 2019 15:30:45 +0100 Subject: [PATCH 03/38] Has tests --- .../clients/csharp/.gitignore | 353 ++++++++++++++++++ .../NiryoOneClient.Tests.csproj | 21 ++ .../NiryoOneConnectionTest.cs | 46 +++ .../clients/csharp/NiryoOneClient.sln | 48 +++ .../csharp/NiryoOneClient/NiryoOneClient.cs | 106 ++---- .../NiryoOneClient/NiryoOneConnection.cs | 74 ++++ .../NiryoOneClient/NiryoOneException.cs | 14 + 7 files changed, 581 insertions(+), 81 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/.gitignore create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs diff --git a/niryo_one_tcp_server/clients/csharp/.gitignore b/niryo_one_tcp_server/clients/csharp/.gitignore new file mode 100644 index 00000000..e6452706 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/.gitignore @@ -0,0 +1,353 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj new file mode 100644 index 00000000..43ee5477 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.0 + + false + + + + + + + + + + + + + + + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs new file mode 100644 index 00000000..fe862543 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -0,0 +1,46 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace NiryoOneClient.Tests +{ + [TestClass] + public class NiryoOneConnectionTest + { + [TestMethod] + public async Task Calibrate_SuccessfulAuto_Works() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + await connection.Calibrate(CalibrateMode.AUTO); + await streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); + } + + [TestMethod] + public async Task Calibrate_SuccessfulManual_Works() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + await connection.Calibrate(CalibrateMode.MANUAL); + await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } + + [TestMethod] + public async Task Calibrate_Failure_Throws() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + var e = await Assert.ThrowsExceptionAsync(async () => await connection.Calibrate(CalibrateMode.MANUAL)); + Assert.AreEqual("Sucks to be sucky", e.Reason); + await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } + } +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln new file mode 100644 index 00000000..3db09dfb --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NiryoOneClient", "NiryoOneClient\NiryoOneClient.csproj", "{10575BA8-3DCD-4A5C-8754-39E893F6043B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NiryoOneClient.Tests", "NiryoOneClient.Tests\NiryoOneClient.Tests.csproj", "{41BDE194-283F-47F8-98A4-978A65393FFC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|x64.ActiveCfg = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|x64.Build.0 = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|x86.ActiveCfg = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Debug|x86.Build.0 = Debug|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|Any CPU.Build.0 = Release|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|x64.ActiveCfg = Release|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|x64.Build.0 = Release|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|x86.ActiveCfg = Release|Any CPU + {10575BA8-3DCD-4A5C-8754-39E893F6043B}.Release|x86.Build.0 = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|x64.Build.0 = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|x86.ActiveCfg = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Debug|x86.Build.0 = Debug|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|Any CPU.Build.0 = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x64.ActiveCfg = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x64.Build.0 = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x86.ActiveCfg = Release|Any CPU + {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index 042901e0..6b57e3a6 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -1,96 +1,40 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net.Sockets; -using System.Runtime.InteropServices.ComTypes; -using System.Text; using System.Threading.Tasks; namespace NiryoOneClient { - public enum CalibrateMode - { - AUTO, - MANUAL - } - - public class NiryoOneClient - { - private TcpClient _client; - private int _port; - private string _server; - - public NiryoOneClient(string server, int port) + public enum CalibrateMode { - _server = server; - _port = port; + AUTO, + MANUAL } - public void Connect() + public class NiryoOneClient { - if (_client != null) - { - _client.Close(); - _client.Dispose(); - } - - _client = new TcpClient(_server, _port); - } + private TcpClient _client; + private int _port; + private string _server; - private string ToSnakeCaseUpper(string s) - { - return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); - } + public NiryoOneClient(string server, int port) + { + _server = server; + _port = port; + } - public async Task Calibrate(CalibrateMode mode) - { - await SendCommandAsync(nameof(Calibrate), mode.ToString()); - await ReceiveAnswerAsync(nameof(Calibrate)); - } - - public async Task SetLearningMode(bool mode) - { - await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); - await ReceiveAnswerAsync(nameof(SetLearningMode)); - } + public async Task Connect() + { + if (_client != null) + { + _client.Close(); + _client.Dispose(); + _client = null; + } - public async Task SendCommandAsync(string command_type, params string[] args) - { - if (!_client.Connected) - throw new SocketException(); - - string cmd; - if (args.Any()) - cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; - else - cmd = ToSnakeCaseUpper(command_type); - - await _client.GetStream().WriteAsync(Encoding.ASCII.GetBytes(cmd + "\n")); - } - - public async Task ReceiveAnswerAsync(string command_type) - { - var sr = new StreamReader(_client.GetStream()); - var result = await sr.ReadLineAsync(); - result = result.TrimEnd('\n'); - var colonSplit = result.Split(':', 2); - var cmd = colonSplit[0]; - if (cmd != ToSnakeCaseUpper(command_type)) - throw new NiryoOneException("Wrong command response received."); - var commaSplit2 = colonSplit[1].Split(':', 2); - var status = commaSplit2[0]; - if (status == "KO") - throw new NiryoOneException(commaSplit2[1]); - - return commaSplit2[1].Split(','); - } - } - - public class NiryoOneException : Exception - { - public NiryoOneException(string reason) - { + _client = new TcpClient(); + await _client.ConnectAsync(_server, _port); + var stream = _client.GetStream(); + return new NiryoOneConnection(new System.IO.StreamReader(stream), new System.IO.StreamWriter(stream)); + } } - } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs new file mode 100644 index 00000000..a14358a5 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -0,0 +1,74 @@ +using System.IO; +using System.Threading.Tasks; +using System.Linq; + +namespace NiryoOneClient +{ + public class NiryoOneConnection + { + private readonly TextWriter _textWriter; + private readonly TextReader _textReader; + + public NiryoOneConnection(TextReader streamReader, TextWriter streamWriter) + { + _textWriter = streamWriter; + _textReader = streamReader; + } + + internal async Task WriteLineAsync(string s) + { + await _textWriter.WriteLineAsync(s); + } + + internal async Task ReadLineAsync() + { + return await _textReader.ReadLineAsync(); + } + + protected string ToSnakeCaseUpper(string s) + { + return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); + } + + protected async Task SendCommandAsync(string command_type, params string[] args) + { + string cmd; + if (args.Any()) + cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; + else + cmd = ToSnakeCaseUpper(command_type); + await WriteLineAsync(cmd); + } + + protected async Task ReceiveAnswerAsync(string command_type) + { + var result = await ReadLineAsync(); + result = result.TrimEnd('\n'); + var colonSplit = result.Split(':', 2); + var cmd = colonSplit[0]; + if (cmd != ToSnakeCaseUpper(command_type)) + throw new NiryoOneException("Wrong command response received."); + var commaSplit2 = colonSplit[1].Split(',', 2); + var status = commaSplit2[0]; + if (status != "OK") + throw new NiryoOneException(commaSplit2[1].TrimStart('"').TrimEnd('"')); + + if (commaSplit2.Length > 1) + return commaSplit2[1].Split(','); + else + return new string[0]; + } + + public async Task Calibrate(CalibrateMode mode) + { + await SendCommandAsync(nameof(Calibrate), mode.ToString()); + await ReceiveAnswerAsync(nameof(Calibrate)); + } + + public async Task SetLearningMode(bool mode) + { + await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); + await ReceiveAnswerAsync(nameof(SetLearningMode)); + } + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs new file mode 100644 index 00000000..617555e2 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs @@ -0,0 +1,14 @@ +using System; + +namespace NiryoOneClient +{ + public class NiryoOneException : Exception + { + public NiryoOneException(string reason) + { + Reason = reason; + } + + public string Reason { get; } + } +} \ No newline at end of file From 76590b813aa7e648cecdec0f460969b6bfcbe8f9 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 1 Nov 2019 17:45:13 +0100 Subject: [PATCH 04/38] LF instead of CRLF --- .../NiryoOneConnectionTest.cs | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index fe862543..b9376675 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -1,46 +1,46 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace NiryoOneClient.Tests -{ - [TestClass] - public class NiryoOneConnectionTest - { - [TestMethod] - public async Task Calibrate_SuccessfulAuto_Works() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - await connection.Calibrate(CalibrateMode.AUTO); - await streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); - } - - [TestMethod] - public async Task Calibrate_SuccessfulManual_Works() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - await connection.Calibrate(CalibrateMode.MANUAL); - await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); - } - - [TestMethod] - public async Task Calibrate_Failure_Throws() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - var e = await Assert.ThrowsExceptionAsync(async () => await connection.Calibrate(CalibrateMode.MANUAL)); - Assert.AreEqual("Sucks to be sucky", e.Reason); - await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); - } - } -} +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace NiryoOneClient.Tests +{ + [TestClass] + public class NiryoOneConnectionTest + { + [TestMethod] + public async Task Calibrate_SuccessfulAuto_Works() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + await connection.Calibrate(CalibrateMode.AUTO); + await streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); + } + + [TestMethod] + public async Task Calibrate_SuccessfulManual_Works() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + await connection.Calibrate(CalibrateMode.MANUAL); + await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } + + [TestMethod] + public async Task Calibrate_Failure_Throws() + { + var streamReader = Substitute.For(); + var streamWriter = Substitute.For(); + streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); + var connection = new NiryoOneConnection(streamReader, streamWriter); + var e = await Assert.ThrowsExceptionAsync(async () => await connection.Calibrate(CalibrateMode.MANUAL)); + Assert.AreEqual("Sucks to be sucky", e.Reason); + await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } + } +} From e3ee9cfcd6542fc4d5d2b253056bee52d8015e7e Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 1 Nov 2019 17:47:29 +0100 Subject: [PATCH 05/38] Simplified tests --- .../NiryoOneConnectionTest.cs | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index b9376675..6b5e0675 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -9,38 +9,49 @@ namespace NiryoOneClient.Tests [TestClass] public class NiryoOneConnectionTest { + private TextReader _streamReader; + private TextWriter _streamWriter; + + private NiryoOneConnection _connection; + + public NiryoOneConnectionTest() + { + _streamReader = Substitute.For(); + _streamWriter = Substitute.For(); + _connection = new NiryoOneConnection(_streamReader, _streamWriter); + } + [TestMethod] public async Task Calibrate_SuccessfulAuto_Works() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - await connection.Calibrate(CalibrateMode.AUTO); - await streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + await _connection.Calibrate(CalibrateMode.AUTO); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); } - + [TestMethod] public async Task Calibrate_SuccessfulManual_Works() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - await connection.Calibrate(CalibrateMode.MANUAL); - await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); - } + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + await _connection.Calibrate(CalibrateMode.MANUAL); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } [TestMethod] public async Task Calibrate_Failure_Throws() - { - var streamReader = Substitute.For(); - var streamWriter = Substitute.For(); - streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); - var connection = new NiryoOneConnection(streamReader, streamWriter); - var e = await Assert.ThrowsExceptionAsync(async () => await connection.Calibrate(CalibrateMode.MANUAL)); + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); - await streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + } + + [TestMethod] + public async Task SetLearningMode_True_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode(true); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } } } From f7c8ef4e90405e8cf052720079c27efc01dd6ff4 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 1 Nov 2019 22:48:36 +0100 Subject: [PATCH 06/38] Add build and test tasks to .vscode/tasks.json --- .../clients/csharp/.vscode/tasks.json | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 niryo_one_tcp_server/clients/csharp/.vscode/tasks.json diff --git a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json new file mode 100644 index 00000000..98ed2477 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/NiryoOneClient.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/NiryoOneClient.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "group": { + "kind": "test", + "isDefault": true + }, + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/NiryoOneClient.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/NiryoOneClient.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file From 4ad2fbefa2777b2a562bd3a6cefd247fad85b469 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Thu, 7 Nov 2019 18:54:22 +0100 Subject: [PATCH 07/38] Add all endpoints, tests and documentation. --- .../HardwareStatusTest.cs | 29 ++ .../NiryoOneConnectionTest.cs | 280 +++++++++++++++++- .../csharp/NiryoOneClient/HardwareStatus.cs | 84 ++++++ .../NiryoOneClient/NiryoOneClient.csproj | 7 +- .../NiryoOneClient/NiryoOneConnection.cs | 237 ++++++++++++++- .../csharp/NiryoOneClient/PoseObject.cs | 51 ++++ .../csharp/NiryoOneClient/RobotJoints.cs | 47 +++ .../clients/csharp/NiryoOneClient/RobotPin.cs | 45 +++ .../clients/csharp/NiryoOneClient/Tools.cs | 11 + 9 files changed, 784 insertions(+), 7 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs new file mode 100644 index 00000000..e4e189fd --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs @@ -0,0 +1,29 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NiryoOneClient.Tests +{ + [TestClass] + public class HardwareStatusTest + { + [TestMethod] + public void Parse_Sample_Works() + { + // Arrange + var s = "[59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)]"; + // Act + var hs = HardwareStatus.Parse(s); + // Assert + Assert.AreEqual(59, hs.RpiTemperature); + Assert.AreEqual(2, hs.HardwareVersion); + Assert.AreEqual(true, hs.ConnectionUp); + Assert.AreEqual("", hs.ErrorMessage); + Assert.AreEqual(0, hs.CalibrationNeeded); + Assert.AreEqual(false, hs.CalibrationInProgress); + CollectionAssert.AreEqual(new[] {"Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6"}, hs.MotorNames); + CollectionAssert.AreEqual(new[] {"Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320"}, hs.MotorTypes); + CollectionAssert.AreEqual(new[] {34, 34, 37, 43, 45, 37}, hs.Temperatures); + CollectionAssert.AreEqual(new[] {0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m}, hs.Voltages); + CollectionAssert.AreEqual(new[] {0, 0, 0, 0, 0, 0}, hs.HardwareErrors); + } + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 6b5e0675..09a13630 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -1,8 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; using System.IO; -using System.Text; using System.Threading.Tasks; +using System.Linq; namespace NiryoOneClient.Tests { @@ -40,7 +40,7 @@ public async Task Calibrate_SuccessfulManual_Works() [TestMethod] public async Task Calibrate_Failure_Throws() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"\n")); + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"")); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); @@ -53,5 +53,281 @@ public async Task SetLearningMode_True_Works() await _connection.SetLearningMode(true); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } + + [TestMethod] + public async Task SetLearningMode_False_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode(false); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + } + + [TestMethod] + public async Task SetLearningMode_WrongResponse_Throws() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("COCO:OK")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); + Assert.AreEqual("Wrong command response received.", e.Reason); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + } + + [TestMethod] + public async Task SetLearningMode_ErrorResponse_Throws() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:KO,\"No good\"")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); + Assert.AreEqual("No good", e.Reason); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + } + + [TestMethod] + public async Task MoveJoints_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_JOINTS:OK")); + await _connection.MoveJoints(new RobotJoints(new[] { + 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f + })); + await _streamWriter.Received().WriteLineAsync("MOVE_JOINTS:0.03,0.0123,0.456,0.987,0.654,0.321"); + } + + [TestMethod] + public async Task MovePose_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_POSE:OK")); + await _connection.MovePose(new PoseObject(new[] { + 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f + })); + await _streamWriter.Received().WriteLineAsync("MOVE_POSE:0.03,0.0123,0.456,0.987,0.654,0.321"); + } + + [TestMethod] + public async Task ShiftPose_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SHIFT_POSE:OK")); + await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); + await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); + } + + [TestMethod] + public async Task SetArmMaxVelocity_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_ARM_MAX_VELOCITY:OK")); + await _connection.SetArmMaxVelocity(50); + await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); + } + + [TestMethod] + public async Task EnableJoystick_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("ENABLE_JOYSTICK:OK")); + await _connection.EnableJoystick(false); + await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); + } + + [TestMethod] + public async Task SetPinMode_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_PIN_MODE:OK")); + await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); + await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); + } + + [TestMethod] + public async Task DigitalWrite_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "DIGITAL_WRITE:OK" + )); + await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); + await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); + } + + [TestMethod] + public async Task DigitalRead_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "DIGITAL_READ:OK,HIGH" + )); + await _connection.DigitalRead(RobotPin.GPIO_1A); + await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); + } + + [TestMethod] + public async Task ChangeTool_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "CHANGE_TOOL:OK" + )); + await _connection.ChangeTool(RobotTool.GRIPPER_2); + await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); + } + + [TestMethod] + public async Task OpenGripper_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "OPEN_GRIPPER:OK" + )); + await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); + await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); + } + + [TestMethod] + public async Task CloseGripper_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "CLOSE_GRIPPER:OK" + )); + await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); + await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); + } + + [TestMethod] + public async Task PullAirVacuumPump_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "PULL_AIR_VACUUM_PUMP:OK" + )); + await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + } + + [TestMethod] + public async Task PushAirVacuumPump_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "PUSH_AIR_VACUUM_PUMP:OK" + )); + await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + } + + [TestMethod] + public async Task SetupElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "SETUP_ELECTROMAGNET:OK" + )); + await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + } + + [TestMethod] + public async Task ActivateElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "ACTIVATE_ELECTROMAGNET:OK" + )); + await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + } + + [TestMethod] + public async Task DeactivateElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "DEACTIVATE_ELECTROMAGNET:OK" + )); + await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); + await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); + } + + [TestMethod] + public async Task GetJoints_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0" + )); + var joints = await _connection.GetJoints(); + CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); + } + + [TestMethod] + public async Task GetPose_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12" + )); + var pose = await _connection.GetPose(); + CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, + pose.ToArray()); + } + + [TestMethod] + public async Task GetHardwareStatus_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)" + )); + var status = await _connection.GetHardwareStatus(); + Assert.AreEqual(59, status.RpiTemperature); + Assert.AreEqual(2, status.HardwareVersion); + Assert.AreEqual(true, status.ConnectionUp); + Assert.AreEqual("", status.ErrorMessage); + Assert.AreEqual(0, status.CalibrationNeeded); + Assert.AreEqual(false, status.CalibrationInProgress); + CollectionAssert.AreEqual(new[] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, status.MotorNames); + CollectionAssert.AreEqual(new[] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, status.MotorTypes); + CollectionAssert.AreEqual(new[] { 34, 34, 37, 43, 45, 37 }, status.Temperatures); + CollectionAssert.AreEqual(new[] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, status.Voltages); + CollectionAssert.AreEqual(new[] { 0, 0, 0, 0, 0, 0 }, status.HardwareErrors); + } + + [TestMethod] + public async Task GetLearningMode_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("GET_LEARNING_MODE:OK,FALSE")); + var mode = await _connection.GetLearningMode(); + await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); + Assert.AreEqual(false, mode); + } + + [TestMethod] + public async Task GetDigitalIoState_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( + "GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]" + )); + var state = await _connection.GetDigitalIoState(); + Assert.AreEqual(2, state[0].PinId); + Assert.AreEqual("1A", state[0].Name); + Assert.AreEqual(PinMode.INPUT, state[0].Mode); + Assert.AreEqual(DigitalState.HIGH, state[0].State); + + Assert.AreEqual(3, state[1].PinId); + Assert.AreEqual("1B", state[1].Name); + Assert.AreEqual(PinMode.INPUT, state[1].Mode); + Assert.AreEqual(DigitalState.HIGH, state[1].State); + + Assert.AreEqual(16, state[2].PinId); + Assert.AreEqual("1C", state[2].Name); + Assert.AreEqual(PinMode.INPUT, state[2].Mode); + Assert.AreEqual(DigitalState.HIGH, state[2].State); + + Assert.AreEqual(26, state[3].PinId); + Assert.AreEqual("2A", state[3].Name); + Assert.AreEqual(PinMode.INPUT, state[3].Mode); + Assert.AreEqual(DigitalState.HIGH, state[3].State); + + Assert.AreEqual(19, state[4].PinId); + Assert.AreEqual("2B", state[4].Name); + Assert.AreEqual(PinMode.INPUT, state[4].Mode); + Assert.AreEqual(DigitalState.HIGH, state[4].State); + + Assert.AreEqual(6, state[5].PinId); + Assert.AreEqual("2C", state[5].Name); + Assert.AreEqual(PinMode.INPUT, state[5].Mode); + Assert.AreEqual(DigitalState.HIGH, state[5].State); + + Assert.AreEqual(12, state[6].PinId); + Assert.AreEqual("SW1", state[6].Name); + Assert.AreEqual(PinMode.OUTPUT, state[6].Mode); + Assert.AreEqual(DigitalState.LOW, state[6].State); + + Assert.AreEqual(13, state[7].PinId); + Assert.AreEqual("SW2", state[7].Name); + Assert.AreEqual(PinMode.OUTPUT, state[7].Mode); + Assert.AreEqual(DigitalState.LOW, state[7].State); + } } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs new file mode 100644 index 00000000..6f3365d8 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using System.Linq; + +namespace NiryoOneClient +{ + public class HardwareStatus + { + private static string Strip_(string s, char prefix, char suffix) + { + if (!s.StartsWith(prefix)) + throw new ArgumentException(); + if (!s.EndsWith(suffix)) + throw new ArgumentException(); + return s.Substring(1, s.Length - 2); + } + + private static string[] ParseStrings_(string s) { + var regex = new System.Text.RegularExpressions.Regex(@"'[^']*'"); + return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); + } + + private static T[] ParseNumbers_(string s, Func parser) { + var regex = new System.Text.RegularExpressions.Regex(@"[0-9]+(\.[0-9]*)?"); + return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); + } + + public static HardwareStatus Parse(string data) + { + var regex = new System.Text.RegularExpressions.Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); + var matches = regex.Matches(data); + + if (matches.Count != 11) + { + throw new NiryoOneException("Incorrect answer received, cannot understand received format."); + } + + var rpiTemperature = int.Parse(matches[0].Value); + var hardwareVersion = int.Parse(matches[1].Value); + var connectionUp = bool.Parse(matches[2].Value); + var errorMessage = Strip_(matches[3].Value, '\'', '\''); + var calibrationNeeded = int.Parse(matches[4].Value); + var calibrationInProgress = bool.Parse(matches[5].Value); + + var motorNames = ParseStrings_(matches[6].Value); + var motorTypes = ParseStrings_(matches[7].Value); + + var temperatures = ParseNumbers_(matches[8].Value, int.Parse); + var voltages = ParseNumbers_(matches[9].Value, decimal.Parse); + var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); + + var hardwareStatus = new HardwareStatus() + { + RpiTemperature = rpiTemperature, + HardwareVersion = hardwareVersion, + ConnectionUp = connectionUp, + ErrorMessage = errorMessage, + CalibrationNeeded = calibrationNeeded, + CalibrationInProgress = calibrationInProgress, + MotorNames = motorNames, + MotorTypes = motorTypes, + Temperatures = temperatures, + Voltages = voltages, + HardwareErrors = hardwareErrors + }; + return hardwareStatus; + } + + + public int RpiTemperature; + public int HardwareVersion; + public bool ConnectionUp; + public string ErrorMessage; + public int CalibrationNeeded; + public bool CalibrationInProgress; + public string[] MotorNames; + public string[] MotorTypes; + public int[] Temperatures; + public decimal[] Voltages; + public int[] HardwareErrors; + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj index d4c395e8..601e5b6c 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj @@ -1,7 +1,12 @@ - netstandard2.1 + netcoreapp3.0 + + + + + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index a14358a5..21879365 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -1,6 +1,10 @@ using System.IO; using System.Threading.Tasks; using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System; +using System.Text.RegularExpressions; namespace NiryoOneClient { @@ -40,7 +44,7 @@ protected async Task SendCommandAsync(string command_type, params string[] args) await WriteLineAsync(cmd); } - protected async Task ReceiveAnswerAsync(string command_type) + protected async Task ReceiveAnswerAsync(string command_type) { var result = await ReadLineAsync(); result = result.TrimEnd('\n'); @@ -54,21 +58,246 @@ protected async Task ReceiveAnswerAsync(string command_type) throw new NiryoOneException(commaSplit2[1].TrimStart('"').TrimEnd('"')); if (commaSplit2.Length > 1) - return commaSplit2[1].Split(','); - else - return new string[0]; + return commaSplit2[1]; + else + return string.Empty; } + /// + /// Request calibration. + /// Whether to request automatic or manual calibration + /// public async Task Calibrate(CalibrateMode mode) { await SendCommandAsync(nameof(Calibrate), mode.ToString()); await ReceiveAnswerAsync(nameof(Calibrate)); } + /// + /// Set whether the robot should be in learning mode or not. + /// Activate learning mode or not + /// public async Task SetLearningMode(bool mode) { await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); await ReceiveAnswerAsync(nameof(SetLearningMode)); } + + /// + /// Move joints to specified configuration. + /// The desired destination joint configuration + /// + public async Task MoveJoints(RobotJoints joints) + { + await SendCommandAsync(nameof(MoveJoints), string.Join(',', joints)); + await ReceiveAnswerAsync(nameof(MoveJoints)); + } + + /// + /// Move joints to specified pose. + /// The desired destination pose + /// + public async Task MovePose(PoseObject pose) + { + await SendCommandAsync(nameof(MovePose), string.Join(',', pose)); + await ReceiveAnswerAsync(nameof(MovePose)); + } + + /// + /// Shift the pose along one axis. + /// Which axis to shift + /// The amount to shift (meters or radians) + /// + public async Task ShiftPose(RobotAxis axis, float value) + { + await SendCommandAsync(nameof(ShiftPose), axis.ToString(), value.ToString()); + await ReceiveAnswerAsync(nameof(ShiftPose)); + } + + /// + /// Set the maximum arm velocity.false + /// The maximum velocity in percent of maximum velocity. + /// + public async Task SetArmMaxVelocity(int velocity) + { + await SendCommandAsync(nameof(SetArmMaxVelocity), velocity.ToString()); + await ReceiveAnswerAsync(nameof(SetArmMaxVelocity)); + } + + /// + /// Enable or disable joystick control.false + /// + public async Task EnableJoystick(bool mode) + { + await SendCommandAsync(nameof(EnableJoystick), mode.ToString().ToUpper()); + await ReceiveAnswerAsync(nameof(EnableJoystick)); + } + + /// + /// Configure a GPIO pin for input or output. + /// + public async Task SetPinMode(RobotPin pin, PinMode mode) + { + await SendCommandAsync(nameof(SetPinMode), pin.ToString(), mode.ToString()); + await ReceiveAnswerAsync(nameof(SetPinMode)); + } + + /// + /// Write to a digital pin configured as output. + /// + public async Task DigitalWrite(RobotPin pin, DigitalState state) + { + await SendCommandAsync(nameof(DigitalWrite), pin.ToString(), state.ToString()); + await ReceiveAnswerAsync(nameof(DigitalWrite)); + } + + /// + /// Read from a digital pin configured as input. + /// + public async Task DigitalRead(RobotPin pin) + { + await SendCommandAsync(nameof(DigitalRead), pin.ToString()); + var state = await ReceiveAnswerAsync(nameof(DigitalRead)); + return (DigitalState)Enum.Parse(typeof(DigitalState), state); + } + + /// + /// Select which tool is connected to the robot. + /// + public async Task ChangeTool(RobotTool tool) + { + await SendCommandAsync(nameof(ChangeTool), tool.ToString()); + await ReceiveAnswerAsync(nameof(ChangeTool)); + } + + /// + /// Open the gripper. + /// Which gripper to open + /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. + /// + public async Task OpenGripper(RobotTool gripper, int speed) + { + await SendCommandAsync(nameof(OpenGripper), gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync(nameof(OpenGripper)); + } + + /// + /// Close the gripper. + /// Which gripper to close + /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. + /// + public async Task CloseGripper(RobotTool gripper, int speed) + { + await SendCommandAsync(nameof(CloseGripper), gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync(nameof(CloseGripper)); + } + + /// + /// Pull air using the vacuum pump. + /// Must be VACUUM_PUMP_1. Only one type available for now. + /// + public async Task PullAirVacuumPump(RobotTool vacuumPump) + { + await SendCommandAsync(nameof(PullAirVacuumPump), vacuumPump.ToString()); + await ReceiveAnswerAsync(nameof(PullAirVacuumPump)); + } + + /// + /// Push air using the vacuum pump. + /// Must be VACUUM_PUMP_1. Only one type available for now. + /// + public async Task PushAirVacuumPump(RobotTool vacuumPump) + { + await SendCommandAsync(nameof(PushAirVacuumPump), vacuumPump.ToString()); + await ReceiveAnswerAsync(nameof(PushAirVacuumPump)); + } + + /// + /// Setup the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(SetupElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(SetupElectromagnet)); + } + + /// + /// Activate the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(ActivateElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(ActivateElectromagnet)); + } + + /// + /// Deactivate the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(DeactivateElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(DeactivateElectromagnet)); + } + + /// + /// Get the current joint configuration. + /// + public async Task GetJoints() + { + await SendCommandAsync(nameof(GetJoints)); + var joints = await ReceiveAnswerAsync(nameof(GetJoints)); + return RobotJoints.Parse(joints); + } + + /// + /// Get the current pose. + /// + public async Task GetPose() + { + await SendCommandAsync(nameof(GetPose)); + var pose = await ReceiveAnswerAsync(nameof(GetPose)); + return PoseObject.Parse(pose); + } + + /// + /// Get the current hardware status. + /// + public async Task GetHardwareStatus() + { + await SendCommandAsync(nameof(GetHardwareStatus)); + var status = await ReceiveAnswerAsync(nameof(GetHardwareStatus)); + return HardwareStatus.Parse(status); + } + + /// + /// Get whether the robot is in learning mode. + /// + public async Task GetLearningMode() + { + await SendCommandAsync(nameof(GetLearningMode)); + var mode = await ReceiveAnswerAsync(nameof(GetLearningMode)); + return bool.Parse(mode); + } + + /// + /// Get the current state of the digital io pins. + /// + public async Task GetDigitalIoState() + { + await SendCommandAsync(nameof(GetDigitalIoState)); + var state = await ReceiveAnswerAsync(nameof(GetDigitalIoState)); + + var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); + var matches = regex.Matches(state); + + return matches.Select(m => DigitalPinObject.Parse(m.Value)).ToArray(); + } + } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs new file mode 100644 index 00000000..48afe8bb --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace NiryoOneClient +{ + public class PoseObject : IEnumerable + { + private float[] _j = new float[6]; + + public PoseObject(float x, float y, float z, float roll, float pitch, float yaw) + { + _j = new[] { x, y, z, roll, pitch, yaw }; + } + + public PoseObject(float[] j) + { + if (j.Length != 6) + throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); + + _j = j; + } + + public static PoseObject Parse(string s) + { + return new PoseObject(s.Split(",").Select(float.Parse).ToArray()); + } + + public float X { get => _j[0]; set => _j[0] = value; } + public float Y { get => _j[1]; set => _j[1] = value; } + public float Z { get => _j[2]; set => _j[2] = value; } + public float Roll { get => _j[3]; set => _j[3] = value; } + public float Pitch { get => _j[4]; set => _j[4] = value; } + public float Yaw { get => _j[5]; set => _j[5] = value; } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_j).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _j.GetEnumerator(); + } + } + + public enum RobotAxis { + X, Y, Z, ROLL, PITCH, YAW + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs new file mode 100644 index 00000000..c5e9e481 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace NiryoOneClient +{ + public class RobotJoints : IEnumerable + { + private float[] _j = new float[6]; + + public RobotJoints(float j1, float j2, float j3, float j4, float j5, float j6) + { + _j = new[] { j1, j2, j3, j4, j5, j6 }; + } + + public RobotJoints(float[] j) + { + if (j.Length != 6) + throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); + + _j = j; + } + + public static RobotJoints Parse(string s) + { + return new RobotJoints(s.Split(",").Select(float.Parse).ToArray()); + } + + public float J1 { get => _j[0]; set => _j[0] = value; } + public float J2 { get => _j[1]; set => _j[1] = value; } + public float J3 { get => _j[2]; set => _j[2] = value; } + public float J4 { get => _j[3]; set => _j[3] = value; } + public float J5 { get => _j[4]; set => _j[4] = value; } + public float J6 { get => _j[5]; set => _j[5] = value; } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_j).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _j.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs new file mode 100644 index 00000000..e35225a2 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace NiryoOneClient +{ + public enum RobotPin + { + GPIO_1A, GPIO_1B, GPIO_1C, GPIO_2A, GPIO_2B, GPIO_2C + } + + public enum PinMode + { + OUTPUT = 0, INPUT = 1 + } + + public enum DigitalState + { + LOW = 0, HIGH = 1 + } + + public class DigitalPinObject + { + public int PinId; + public string Name; + public PinMode Mode; + public DigitalState State; + + public static DigitalPinObject Parse(string s) + { + if (!s.StartsWith('[') || !s.EndsWith(']')) + throw new ArgumentException(); + + var ss = s.Substring(1, s.Length - 2).Split(", "); + + return new DigitalPinObject + { + PinId = int.Parse(ss[0]), + Name = ss[1].Trim().Substring(1, ss[1].Length - 2), + Mode = (PinMode)int.Parse(ss[2]), + State = (DigitalState)int.Parse(ss[3]) + }; + } + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs new file mode 100644 index 00000000..11772996 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace NiryoOneClient +{ + public enum RobotTool + { + GRIPPER_1, GRIPPER_2, GRIPPER_3, VACUUM_PUMP_1, ELECTROMAGNET_1 + } +} \ No newline at end of file From e256690c4b4a5125136efdc102c812896f733493 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 09:05:43 +0100 Subject: [PATCH 08/38] MIT License and nuget --- .../HardwareStatusTest.cs | 56 ++- .../NiryoOneConnectionTest.cs | 414 +++++++++--------- .../csharp/NiryoOneClient/HardwareStatus.cs | 96 ++-- .../csharp/NiryoOneClient/NiryoOneClient.cs | 57 ++- .../NiryoOneClient/NiryoOneClient.csproj | 9 + .../NiryoOneClient/NiryoOneConnection.cs | 259 ++++++----- .../NiryoOneClient/NiryoOneException.cs | 32 +- .../csharp/NiryoOneClient/PoseObject.cs | 61 ++- .../csharp/NiryoOneClient/RobotJoints.cs | 54 ++- .../clients/csharp/NiryoOneClient/RobotPin.cs | 69 ++- .../csharp/NiryoOneClient/RobotTool.cs | 36 ++ .../clients/csharp/NiryoOneClient/Tools.cs | 11 - 12 files changed, 651 insertions(+), 503 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs delete mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs index e4e189fd..4609c14b 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs @@ -1,29 +1,49 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace NiryoOneClient.Tests -{ +namespace NiryoOneClient.Tests { [TestClass] - public class HardwareStatusTest - { + public class HardwareStatusTest { [TestMethod] - public void Parse_Sample_Works() - { + public void Parse_Sample_Works () { // Arrange var s = "[59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)]"; // Act - var hs = HardwareStatus.Parse(s); + var hs = HardwareStatus.Parse (s); // Assert - Assert.AreEqual(59, hs.RpiTemperature); - Assert.AreEqual(2, hs.HardwareVersion); - Assert.AreEqual(true, hs.ConnectionUp); - Assert.AreEqual("", hs.ErrorMessage); - Assert.AreEqual(0, hs.CalibrationNeeded); - Assert.AreEqual(false, hs.CalibrationInProgress); - CollectionAssert.AreEqual(new[] {"Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6"}, hs.MotorNames); - CollectionAssert.AreEqual(new[] {"Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320"}, hs.MotorTypes); - CollectionAssert.AreEqual(new[] {34, 34, 37, 43, 45, 37}, hs.Temperatures); - CollectionAssert.AreEqual(new[] {0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m}, hs.Voltages); - CollectionAssert.AreEqual(new[] {0, 0, 0, 0, 0, 0}, hs.HardwareErrors); + Assert.AreEqual (59, hs.RpiTemperature); + Assert.AreEqual (2, hs.HardwareVersion); + Assert.AreEqual (true, hs.ConnectionUp); + Assert.AreEqual ("", hs.ErrorMessage); + Assert.AreEqual (0, hs.CalibrationNeeded); + Assert.AreEqual (false, hs.CalibrationInProgress); + CollectionAssert.AreEqual (new [] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, hs.MotorNames); + CollectionAssert.AreEqual (new [] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, hs.MotorTypes); + CollectionAssert.AreEqual (new [] { 34, 34, 37, 43, 45, 37 }, hs.Temperatures); + CollectionAssert.AreEqual (new [] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, hs.Voltages); + CollectionAssert.AreEqual (new [] { 0, 0, 0, 0, 0, 0 }, hs.HardwareErrors); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 09a13630..fd54b9a9 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -1,333 +1,325 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System.IO; -using System.Threading.Tasks; using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; -namespace NiryoOneClient.Tests -{ +namespace NiryoOneClient.Tests { [TestClass] - public class NiryoOneConnectionTest - { + public class NiryoOneConnectionTest { private TextReader _streamReader; private TextWriter _streamWriter; private NiryoOneConnection _connection; - public NiryoOneConnectionTest() - { - _streamReader = Substitute.For(); - _streamWriter = Substitute.For(); - _connection = new NiryoOneConnection(_streamReader, _streamWriter); + public NiryoOneConnectionTest () { + _streamReader = Substitute.For (); + _streamWriter = Substitute.For (); + _connection = new NiryoOneConnection (_streamReader, _streamWriter); } [TestMethod] - public async Task Calibrate_SuccessfulAuto_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - await _connection.Calibrate(CalibrateMode.AUTO); - await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); + public async Task Calibrate_SuccessfulAuto_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:OK\n")); + await _connection.Calibrate (CalibrateMode.AUTO); + await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:AUTO"); } [TestMethod] - public async Task Calibrate_SuccessfulManual_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); - await _connection.Calibrate(CalibrateMode.MANUAL); - await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + public async Task Calibrate_SuccessfulManual_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:OK\n")); + await _connection.Calibrate (CalibrateMode.MANUAL); + await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:MANUAL"); } [TestMethod] - public async Task Calibrate_Failure_Throws() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"")); - var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); - Assert.AreEqual("Sucks to be sucky", e.Reason); - await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); + public async Task Calibrate_Failure_Throws () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:KO,\"Sucks to be sucky\"")); + var e = await Assert.ThrowsExceptionAsync (async () => await _connection.Calibrate (CalibrateMode.MANUAL)); + Assert.AreEqual ("Sucks to be sucky", e.Reason); + await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:MANUAL"); } [TestMethod] - public async Task SetLearningMode_True_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); - await _connection.SetLearningMode(true); - await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); + public async Task SetLearningMode_True_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode (true); + await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:TRUE"); } [TestMethod] - public async Task SetLearningMode_False_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); - await _connection.SetLearningMode(false); - await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_False_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode (false); + await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task SetLearningMode_WrongResponse_Throws() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("COCO:OK")); - var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); - Assert.AreEqual("Wrong command response received.", e.Reason); - await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_WrongResponse_Throws () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("COCO:OK")); + var e = await Assert.ThrowsExceptionAsync (async () => await _connection.SetLearningMode (false)); + Assert.AreEqual ("Wrong command response received.", e.Reason); + await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task SetLearningMode_ErrorResponse_Throws() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:KO,\"No good\"")); - var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); - Assert.AreEqual("No good", e.Reason); - await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_ErrorResponse_Throws () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:KO,\"No good\"")); + var e = await Assert.ThrowsExceptionAsync (async () => await _connection.SetLearningMode (false)); + Assert.AreEqual ("No good", e.Reason); + await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task MoveJoints_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_JOINTS:OK")); - await _connection.MoveJoints(new RobotJoints(new[] { - 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f - })); - await _streamWriter.Received().WriteLineAsync("MOVE_JOINTS:0.03,0.0123,0.456,0.987,0.654,0.321"); + public async Task MoveJoints_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("MOVE_JOINTS:OK")); + await _connection.MoveJoints (new RobotJoints (new [] { + 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f + })); + await _streamWriter.Received ().WriteLineAsync ("MOVE_JOINTS:0.03,0.0123,0.456,0.987,0.654,0.321"); } [TestMethod] - public async Task MovePose_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_POSE:OK")); - await _connection.MovePose(new PoseObject(new[] { - 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f - })); - await _streamWriter.Received().WriteLineAsync("MOVE_POSE:0.03,0.0123,0.456,0.987,0.654,0.321"); + public async Task MovePose_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("MOVE_POSE:OK")); + await _connection.MovePose (new PoseObject (new [] { + 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f + })); + await _streamWriter.Received ().WriteLineAsync ("MOVE_POSE:0.03,0.0123,0.456,0.987,0.654,0.321"); } [TestMethod] - public async Task ShiftPose_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SHIFT_POSE:OK")); - await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); - await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); + public async Task ShiftPose_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SHIFT_POSE:OK")); + await _connection.ShiftPose (RobotAxis.ROLL, 0.03142f); + await _streamWriter.Received ().WriteLineAsync ("SHIFT_POSE:ROLL,0.03142"); } [TestMethod] - public async Task SetArmMaxVelocity_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_ARM_MAX_VELOCITY:OK")); - await _connection.SetArmMaxVelocity(50); - await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); + public async Task SetArmMaxVelocity_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_ARM_MAX_VELOCITY:OK")); + await _connection.SetArmMaxVelocity (50); + await _streamWriter.Received ().WriteLineAsync ("SET_ARM_MAX_VELOCITY:50"); } [TestMethod] - public async Task EnableJoystick_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("ENABLE_JOYSTICK:OK")); - await _connection.EnableJoystick(false); - await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); + public async Task EnableJoystick_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("ENABLE_JOYSTICK:OK")); + await _connection.EnableJoystick (false); + await _streamWriter.Received ().WriteLineAsync ("ENABLE_JOYSTICK:FALSE"); } [TestMethod] - public async Task SetPinMode_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_PIN_MODE:OK")); - await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); - await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); + public async Task SetPinMode_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_PIN_MODE:OK")); + await _connection.SetPinMode (RobotPin.GPIO_2B, PinMode.OUTPUT); + await _streamWriter.Received ().WriteLineAsync ("SET_PIN_MODE:GPIO_2B,OUTPUT"); } [TestMethod] - public async Task DigitalWrite_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task DigitalWrite_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "DIGITAL_WRITE:OK" )); - await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); - await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); + await _connection.DigitalWrite (RobotPin.GPIO_2A, DigitalState.LOW); + await _streamWriter.Received ().WriteLineAsync ("DIGITAL_WRITE:GPIO_2A,LOW"); } [TestMethod] - public async Task DigitalRead_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task DigitalRead_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "DIGITAL_READ:OK,HIGH" )); - await _connection.DigitalRead(RobotPin.GPIO_1A); - await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); + await _connection.DigitalRead (RobotPin.GPIO_1A); + await _streamWriter.Received ().WriteLineAsync ("DIGITAL_READ:GPIO_1A"); } [TestMethod] - public async Task ChangeTool_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task ChangeTool_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "CHANGE_TOOL:OK" )); - await _connection.ChangeTool(RobotTool.GRIPPER_2); - await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); + await _connection.ChangeTool (RobotTool.GRIPPER_2); + await _streamWriter.Received ().WriteLineAsync ("CHANGE_TOOL:GRIPPER_2"); } [TestMethod] - public async Task OpenGripper_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task OpenGripper_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "OPEN_GRIPPER:OK" )); - await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); - await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); + await _connection.OpenGripper (RobotTool.GRIPPER_1, 200); + await _streamWriter.Received ().WriteLineAsync ("OPEN_GRIPPER:GRIPPER_1,200"); } [TestMethod] - public async Task CloseGripper_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task CloseGripper_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "CLOSE_GRIPPER:OK" )); - await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); - await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); + await _connection.CloseGripper (RobotTool.GRIPPER_1, 200); + await _streamWriter.Received ().WriteLineAsync ("CLOSE_GRIPPER:GRIPPER_1,200"); } [TestMethod] - public async Task PullAirVacuumPump_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task PullAirVacuumPump_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "PULL_AIR_VACUUM_PUMP:OK" )); - await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); - await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + await _connection.PullAirVacuumPump (RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received ().WriteLineAsync ("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } [TestMethod] - public async Task PushAirVacuumPump_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task PushAirVacuumPump_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "PUSH_AIR_VACUUM_PUMP:OK" )); - await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); - await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + await _connection.PushAirVacuumPump (RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received ().WriteLineAsync ("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } [TestMethod] - public async Task SetupElectromagnet_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task SetupElectromagnet_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "SETUP_ELECTROMAGNET:OK" )); - await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); - await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + await _connection.SetupElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received ().WriteLineAsync ("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } [TestMethod] - public async Task ActivateElectromagnet_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task ActivateElectromagnet_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "ACTIVATE_ELECTROMAGNET:OK" )); - await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); - await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + await _connection.ActivateElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received ().WriteLineAsync ("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } [TestMethod] - public async Task DeactivateElectromagnet_Sample_SendsCorrectly() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task DeactivateElectromagnet_Sample_SendsCorrectly () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "DEACTIVATE_ELECTROMAGNET:OK" )); - await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); - await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); + await _connection.DeactivateElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); + await _streamWriter.Received ().WriteLineAsync ("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); } [TestMethod] - public async Task GetJoints_Sample_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task GetJoints_Sample_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0" )); - var joints = await _connection.GetJoints(); - CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); + var joints = await _connection.GetJoints (); + CollectionAssert.AreEqual (new [] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray ()); } [TestMethod] - public async Task GetPose_Sample_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task GetPose_Sample_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12" )); - var pose = await _connection.GetPose(); - CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, - pose.ToArray()); + var pose = await _connection.GetPose (); + CollectionAssert.AreEqual (new [] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, + pose.ToArray ()); } [TestMethod] - public async Task GetHardwareStatus_Sample_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task GetHardwareStatus_Sample_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)" )); - var status = await _connection.GetHardwareStatus(); - Assert.AreEqual(59, status.RpiTemperature); - Assert.AreEqual(2, status.HardwareVersion); - Assert.AreEqual(true, status.ConnectionUp); - Assert.AreEqual("", status.ErrorMessage); - Assert.AreEqual(0, status.CalibrationNeeded); - Assert.AreEqual(false, status.CalibrationInProgress); - CollectionAssert.AreEqual(new[] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, status.MotorNames); - CollectionAssert.AreEqual(new[] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, status.MotorTypes); - CollectionAssert.AreEqual(new[] { 34, 34, 37, 43, 45, 37 }, status.Temperatures); - CollectionAssert.AreEqual(new[] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, status.Voltages); - CollectionAssert.AreEqual(new[] { 0, 0, 0, 0, 0, 0 }, status.HardwareErrors); + var status = await _connection.GetHardwareStatus (); + Assert.AreEqual (59, status.RpiTemperature); + Assert.AreEqual (2, status.HardwareVersion); + Assert.AreEqual (true, status.ConnectionUp); + Assert.AreEqual ("", status.ErrorMessage); + Assert.AreEqual (0, status.CalibrationNeeded); + Assert.AreEqual (false, status.CalibrationInProgress); + CollectionAssert.AreEqual (new [] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, status.MotorNames); + CollectionAssert.AreEqual (new [] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, status.MotorTypes); + CollectionAssert.AreEqual (new [] { 34, 34, 37, 43, 45, 37 }, status.Temperatures); + CollectionAssert.AreEqual (new [] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, status.Voltages); + CollectionAssert.AreEqual (new [] { 0, 0, 0, 0, 0, 0 }, status.HardwareErrors); } [TestMethod] - public async Task GetLearningMode_Sample_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult("GET_LEARNING_MODE:OK,FALSE")); - var mode = await _connection.GetLearningMode(); - await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); - Assert.AreEqual(false, mode); + public async Task GetLearningMode_Sample_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ("GET_LEARNING_MODE:OK,FALSE")); + var mode = await _connection.GetLearningMode (); + await _streamWriter.Received ().WriteLineAsync ("GET_LEARNING_MODE"); + Assert.AreEqual (false, mode); } [TestMethod] - public async Task GetDigitalIoState_Sample_Works() - { - _streamReader.ReadLineAsync().Returns(Task.FromResult( + public async Task GetDigitalIoState_Sample_Works () { + _streamReader.ReadLineAsync ().Returns (Task.FromResult ( "GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]" - )); - var state = await _connection.GetDigitalIoState(); - Assert.AreEqual(2, state[0].PinId); - Assert.AreEqual("1A", state[0].Name); - Assert.AreEqual(PinMode.INPUT, state[0].Mode); - Assert.AreEqual(DigitalState.HIGH, state[0].State); - - Assert.AreEqual(3, state[1].PinId); - Assert.AreEqual("1B", state[1].Name); - Assert.AreEqual(PinMode.INPUT, state[1].Mode); - Assert.AreEqual(DigitalState.HIGH, state[1].State); - - Assert.AreEqual(16, state[2].PinId); - Assert.AreEqual("1C", state[2].Name); - Assert.AreEqual(PinMode.INPUT, state[2].Mode); - Assert.AreEqual(DigitalState.HIGH, state[2].State); - - Assert.AreEqual(26, state[3].PinId); - Assert.AreEqual("2A", state[3].Name); - Assert.AreEqual(PinMode.INPUT, state[3].Mode); - Assert.AreEqual(DigitalState.HIGH, state[3].State); - - Assert.AreEqual(19, state[4].PinId); - Assert.AreEqual("2B", state[4].Name); - Assert.AreEqual(PinMode.INPUT, state[4].Mode); - Assert.AreEqual(DigitalState.HIGH, state[4].State); - - Assert.AreEqual(6, state[5].PinId); - Assert.AreEqual("2C", state[5].Name); - Assert.AreEqual(PinMode.INPUT, state[5].Mode); - Assert.AreEqual(DigitalState.HIGH, state[5].State); - - Assert.AreEqual(12, state[6].PinId); - Assert.AreEqual("SW1", state[6].Name); - Assert.AreEqual(PinMode.OUTPUT, state[6].Mode); - Assert.AreEqual(DigitalState.LOW, state[6].State); - - Assert.AreEqual(13, state[7].PinId); - Assert.AreEqual("SW2", state[7].Name); - Assert.AreEqual(PinMode.OUTPUT, state[7].Mode); - Assert.AreEqual(DigitalState.LOW, state[7].State); + )); + var state = await _connection.GetDigitalIoState (); + Assert.AreEqual (2, state[0].PinId); + Assert.AreEqual ("1A", state[0].Name); + Assert.AreEqual (PinMode.INPUT, state[0].Mode); + Assert.AreEqual (DigitalState.HIGH, state[0].State); + + Assert.AreEqual (3, state[1].PinId); + Assert.AreEqual ("1B", state[1].Name); + Assert.AreEqual (PinMode.INPUT, state[1].Mode); + Assert.AreEqual (DigitalState.HIGH, state[1].State); + + Assert.AreEqual (16, state[2].PinId); + Assert.AreEqual ("1C", state[2].Name); + Assert.AreEqual (PinMode.INPUT, state[2].Mode); + Assert.AreEqual (DigitalState.HIGH, state[2].State); + + Assert.AreEqual (26, state[3].PinId); + Assert.AreEqual ("2A", state[3].Name); + Assert.AreEqual (PinMode.INPUT, state[3].Mode); + Assert.AreEqual (DigitalState.HIGH, state[3].State); + + Assert.AreEqual (19, state[4].PinId); + Assert.AreEqual ("2B", state[4].Name); + Assert.AreEqual (PinMode.INPUT, state[4].Mode); + Assert.AreEqual (DigitalState.HIGH, state[4].State); + + Assert.AreEqual (6, state[5].PinId); + Assert.AreEqual ("2C", state[5].Name); + Assert.AreEqual (PinMode.INPUT, state[5].Mode); + Assert.AreEqual (DigitalState.HIGH, state[5].State); + + Assert.AreEqual (12, state[6].PinId); + Assert.AreEqual ("SW1", state[6].Name); + Assert.AreEqual (PinMode.OUTPUT, state[6].Mode); + Assert.AreEqual (DigitalState.LOW, state[6].State); + + Assert.AreEqual (13, state[7].PinId); + Assert.AreEqual ("SW2", state[7].Name); + Assert.AreEqual (PinMode.OUTPUT, state[7].Mode); + Assert.AreEqual (DigitalState.LOW, state[7].State); } } -} +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index 6f3365d8..bac74bf7 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -1,58 +1,75 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System; using System.Collections; using System.Collections.Generic; -using System.Text.Json; using System.Linq; +using System.Text.Json; -namespace NiryoOneClient -{ - public class HardwareStatus - { - private static string Strip_(string s, char prefix, char suffix) - { - if (!s.StartsWith(prefix)) - throw new ArgumentException(); - if (!s.EndsWith(suffix)) - throw new ArgumentException(); - return s.Substring(1, s.Length - 2); +namespace NiryoOneClient { + public class HardwareStatus { + private static string Strip_ (string s, char prefix, char suffix) { + if (!s.StartsWith (prefix)) + throw new ArgumentException (); + if (!s.EndsWith (suffix)) + throw new ArgumentException (); + return s.Substring (1, s.Length - 2); } - - private static string[] ParseStrings_(string s) { - var regex = new System.Text.RegularExpressions.Regex(@"'[^']*'"); - return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); + + private static string[] ParseStrings_ (string s) { + var regex = new System.Text.RegularExpressions.Regex (@"'[^']*'"); + return regex.Matches (s).Select (m => Strip_ (m.Value, '\'', '\'')).ToArray (); } - private static T[] ParseNumbers_(string s, Func parser) { - var regex = new System.Text.RegularExpressions.Regex(@"[0-9]+(\.[0-9]*)?"); - return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); + private static T[] ParseNumbers_ (string s, Func parser) { + var regex = new System.Text.RegularExpressions.Regex (@"[0-9]+(\.[0-9]*)?"); + return regex.Matches (s).Select (m => parser (m.Value)).ToArray (); } - public static HardwareStatus Parse(string data) - { - var regex = new System.Text.RegularExpressions.Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); - var matches = regex.Matches(data); + public static HardwareStatus Parse (string data) { + var regex = new System.Text.RegularExpressions.Regex (@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); + var matches = regex.Matches (data); - if (matches.Count != 11) - { - throw new NiryoOneException("Incorrect answer received, cannot understand received format."); + if (matches.Count != 11) { + throw new NiryoOneException ("Incorrect answer received, cannot understand received format."); } - var rpiTemperature = int.Parse(matches[0].Value); - var hardwareVersion = int.Parse(matches[1].Value); - var connectionUp = bool.Parse(matches[2].Value); - var errorMessage = Strip_(matches[3].Value, '\'', '\''); - var calibrationNeeded = int.Parse(matches[4].Value); - var calibrationInProgress = bool.Parse(matches[5].Value); + var rpiTemperature = int.Parse (matches[0].Value); + var hardwareVersion = int.Parse (matches[1].Value); + var connectionUp = bool.Parse (matches[2].Value); + var errorMessage = Strip_ (matches[3].Value, '\'', '\''); + var calibrationNeeded = int.Parse (matches[4].Value); + var calibrationInProgress = bool.Parse (matches[5].Value); - var motorNames = ParseStrings_(matches[6].Value); - var motorTypes = ParseStrings_(matches[7].Value); + var motorNames = ParseStrings_ (matches[6].Value); + var motorTypes = ParseStrings_ (matches[7].Value); - var temperatures = ParseNumbers_(matches[8].Value, int.Parse); - var voltages = ParseNumbers_(matches[9].Value, decimal.Parse); - var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); + var temperatures = ParseNumbers_ (matches[8].Value, int.Parse); + var voltages = ParseNumbers_ (matches[9].Value, decimal.Parse); + var hardwareErrors = ParseNumbers_ (matches[10].Value, int.Parse); - var hardwareStatus = new HardwareStatus() - { + var hardwareStatus = new HardwareStatus () { RpiTemperature = rpiTemperature, HardwareVersion = hardwareVersion, ConnectionUp = connectionUp, @@ -68,7 +85,6 @@ public static HardwareStatus Parse(string data) return hardwareStatus; } - public int RpiTemperature; public int HardwareVersion; public bool ConnectionUp; diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index 6b57e3a6..25c8c59a 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -1,40 +1,57 @@ -using System; +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System; using System.Net.Sockets; using System.Threading.Tasks; -namespace NiryoOneClient -{ - public enum CalibrateMode - { +namespace NiryoOneClient { + public enum CalibrateMode { AUTO, MANUAL } - public class NiryoOneClient - { + public class NiryoOneClient { private TcpClient _client; private int _port; private string _server; - public NiryoOneClient(string server, int port) - { + public NiryoOneClient (string server, int port) { _server = server; _port = port; } - public async Task Connect() - { - if (_client != null) - { - _client.Close(); - _client.Dispose(); + public async Task Connect () { + if (_client != null) { + _client.Close (); + _client.Dispose (); _client = null; } - _client = new TcpClient(); - await _client.ConnectAsync(_server, _port); - var stream = _client.GetStream(); - return new NiryoOneConnection(new System.IO.StreamReader(stream), new System.IO.StreamWriter(stream)); + _client = new TcpClient (); + await _client.ConnectAsync (_server, _port); + var stream = _client.GetStream (); + return new NiryoOneConnection (new System.IO.StreamReader (stream), new System.IO.StreamWriter (stream)); } } -} +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj index 601e5b6c..ea8231f2 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj @@ -4,6 +4,15 @@ netcoreapp3.0 + + NiryoOneClient + 0.1.0 + rickardraysearch + Niryo + MIT + A tcp client in C# that manages the tcp communication with a Niryo One robot + + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 21879365..f8a81d20 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -1,61 +1,76 @@ -using System.IO; -using System.Threading.Tasks; -using System.Linq; +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System; using System.Collections; using System.Collections.Generic; -using System; +using System.IO; +using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; -namespace NiryoOneClient -{ - public class NiryoOneConnection - { +namespace NiryoOneClient { + public class NiryoOneConnection { private readonly TextWriter _textWriter; private readonly TextReader _textReader; - public NiryoOneConnection(TextReader streamReader, TextWriter streamWriter) - { + public NiryoOneConnection (TextReader streamReader, TextWriter streamWriter) { _textWriter = streamWriter; _textReader = streamReader; } - internal async Task WriteLineAsync(string s) - { - await _textWriter.WriteLineAsync(s); + internal async Task WriteLineAsync (string s) { + await _textWriter.WriteLineAsync (s); } - internal async Task ReadLineAsync() - { - return await _textReader.ReadLineAsync(); + internal async Task ReadLineAsync () { + return await _textReader.ReadLineAsync (); } - protected string ToSnakeCaseUpper(string s) - { - return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); + protected string ToSnakeCaseUpper (string s) { + return string.Concat (s.Select ((x, i) => i > 0 && char.IsUpper (x) ? "_" + x.ToString () : x.ToString ())).ToUpper (); } - protected async Task SendCommandAsync(string command_type, params string[] args) - { + protected async Task SendCommandAsync (string command_type, params string[] args) { string cmd; - if (args.Any()) + if (args.Any ()) cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; else - cmd = ToSnakeCaseUpper(command_type); - await WriteLineAsync(cmd); + cmd = ToSnakeCaseUpper (command_type); + await WriteLineAsync (cmd); } - protected async Task ReceiveAnswerAsync(string command_type) - { - var result = await ReadLineAsync(); - result = result.TrimEnd('\n'); - var colonSplit = result.Split(':', 2); + protected async Task ReceiveAnswerAsync (string command_type) { + var result = await ReadLineAsync (); + result = result.TrimEnd ('\n'); + var colonSplit = result.Split (':', 2); var cmd = colonSplit[0]; - if (cmd != ToSnakeCaseUpper(command_type)) - throw new NiryoOneException("Wrong command response received."); - var commaSplit2 = colonSplit[1].Split(',', 2); + if (cmd != ToSnakeCaseUpper (command_type)) + throw new NiryoOneException ("Wrong command response received."); + var commaSplit2 = colonSplit[1].Split (',', 2); var status = commaSplit2[0]; if (status != "OK") - throw new NiryoOneException(commaSplit2[1].TrimStart('"').TrimEnd('"')); + throw new NiryoOneException (commaSplit2[1].TrimStart ('"').TrimEnd ('"')); if (commaSplit2.Length > 1) return commaSplit2[1]; @@ -67,40 +82,36 @@ protected async Task ReceiveAnswerAsync(string command_type) /// Request calibration. /// Whether to request automatic or manual calibration /// - public async Task Calibrate(CalibrateMode mode) - { - await SendCommandAsync(nameof(Calibrate), mode.ToString()); - await ReceiveAnswerAsync(nameof(Calibrate)); + public async Task Calibrate (CalibrateMode mode) { + await SendCommandAsync (nameof (Calibrate), mode.ToString ()); + await ReceiveAnswerAsync (nameof (Calibrate)); } /// /// Set whether the robot should be in learning mode or not. /// Activate learning mode or not /// - public async Task SetLearningMode(bool mode) - { - await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); - await ReceiveAnswerAsync(nameof(SetLearningMode)); + public async Task SetLearningMode (bool mode) { + await SendCommandAsync (nameof (SetLearningMode), mode.ToString ().ToUpper ()); + await ReceiveAnswerAsync (nameof (SetLearningMode)); } /// /// Move joints to specified configuration. /// The desired destination joint configuration /// - public async Task MoveJoints(RobotJoints joints) - { - await SendCommandAsync(nameof(MoveJoints), string.Join(',', joints)); - await ReceiveAnswerAsync(nameof(MoveJoints)); + public async Task MoveJoints (RobotJoints joints) { + await SendCommandAsync (nameof (MoveJoints), string.Join (',', joints)); + await ReceiveAnswerAsync (nameof (MoveJoints)); } /// /// Move joints to specified pose. /// The desired destination pose /// - public async Task MovePose(PoseObject pose) - { - await SendCommandAsync(nameof(MovePose), string.Join(',', pose)); - await ReceiveAnswerAsync(nameof(MovePose)); + public async Task MovePose (PoseObject pose) { + await SendCommandAsync (nameof (MovePose), string.Join (',', pose)); + await ReceiveAnswerAsync (nameof (MovePose)); } /// @@ -108,66 +119,59 @@ public async Task MovePose(PoseObject pose) /// Which axis to shift /// The amount to shift (meters or radians) /// - public async Task ShiftPose(RobotAxis axis, float value) - { - await SendCommandAsync(nameof(ShiftPose), axis.ToString(), value.ToString()); - await ReceiveAnswerAsync(nameof(ShiftPose)); + public async Task ShiftPose (RobotAxis axis, float value) { + await SendCommandAsync (nameof (ShiftPose), axis.ToString (), value.ToString ()); + await ReceiveAnswerAsync (nameof (ShiftPose)); } /// /// Set the maximum arm velocity.false /// The maximum velocity in percent of maximum velocity. /// - public async Task SetArmMaxVelocity(int velocity) - { - await SendCommandAsync(nameof(SetArmMaxVelocity), velocity.ToString()); - await ReceiveAnswerAsync(nameof(SetArmMaxVelocity)); + public async Task SetArmMaxVelocity (int velocity) { + await SendCommandAsync (nameof (SetArmMaxVelocity), velocity.ToString ()); + await ReceiveAnswerAsync (nameof (SetArmMaxVelocity)); } /// /// Enable or disable joystick control.false /// - public async Task EnableJoystick(bool mode) - { - await SendCommandAsync(nameof(EnableJoystick), mode.ToString().ToUpper()); - await ReceiveAnswerAsync(nameof(EnableJoystick)); + public async Task EnableJoystick (bool mode) { + await SendCommandAsync (nameof (EnableJoystick), mode.ToString ().ToUpper ()); + await ReceiveAnswerAsync (nameof (EnableJoystick)); } /// /// Configure a GPIO pin for input or output. /// - public async Task SetPinMode(RobotPin pin, PinMode mode) - { - await SendCommandAsync(nameof(SetPinMode), pin.ToString(), mode.ToString()); - await ReceiveAnswerAsync(nameof(SetPinMode)); + public async Task SetPinMode (RobotPin pin, PinMode mode) { + await SendCommandAsync (nameof (SetPinMode), pin.ToString (), mode.ToString ()); + await ReceiveAnswerAsync (nameof (SetPinMode)); } /// /// Write to a digital pin configured as output. /// - public async Task DigitalWrite(RobotPin pin, DigitalState state) - { - await SendCommandAsync(nameof(DigitalWrite), pin.ToString(), state.ToString()); - await ReceiveAnswerAsync(nameof(DigitalWrite)); + public async Task DigitalWrite (RobotPin pin, DigitalState state) { + await SendCommandAsync (nameof (DigitalWrite), pin.ToString (), state.ToString ()); + await ReceiveAnswerAsync (nameof (DigitalWrite)); } /// /// Read from a digital pin configured as input. /// - public async Task DigitalRead(RobotPin pin) - { - await SendCommandAsync(nameof(DigitalRead), pin.ToString()); - var state = await ReceiveAnswerAsync(nameof(DigitalRead)); - return (DigitalState)Enum.Parse(typeof(DigitalState), state); + public async Task DigitalRead (RobotPin pin) { + await SendCommandAsync (nameof (DigitalRead), pin.ToString ()); + var state = await ReceiveAnswerAsync (nameof (DigitalRead)); + return (DigitalState) Enum.Parse (typeof (DigitalState), state); } /// /// Select which tool is connected to the robot. /// - public async Task ChangeTool(RobotTool tool) - { - await SendCommandAsync(nameof(ChangeTool), tool.ToString()); - await ReceiveAnswerAsync(nameof(ChangeTool)); + public async Task ChangeTool (RobotTool tool) { + await SendCommandAsync (nameof (ChangeTool), tool.ToString ()); + await ReceiveAnswerAsync (nameof (ChangeTool)); } /// @@ -175,10 +179,9 @@ public async Task ChangeTool(RobotTool tool) /// Which gripper to open /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. /// - public async Task OpenGripper(RobotTool gripper, int speed) - { - await SendCommandAsync(nameof(OpenGripper), gripper.ToString(), speed.ToString()); - await ReceiveAnswerAsync(nameof(OpenGripper)); + public async Task OpenGripper (RobotTool gripper, int speed) { + await SendCommandAsync (nameof (OpenGripper), gripper.ToString (), speed.ToString ()); + await ReceiveAnswerAsync (nameof (OpenGripper)); } /// @@ -186,30 +189,27 @@ public async Task OpenGripper(RobotTool gripper, int speed) /// Which gripper to close /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. /// - public async Task CloseGripper(RobotTool gripper, int speed) - { - await SendCommandAsync(nameof(CloseGripper), gripper.ToString(), speed.ToString()); - await ReceiveAnswerAsync(nameof(CloseGripper)); + public async Task CloseGripper (RobotTool gripper, int speed) { + await SendCommandAsync (nameof (CloseGripper), gripper.ToString (), speed.ToString ()); + await ReceiveAnswerAsync (nameof (CloseGripper)); } /// /// Pull air using the vacuum pump. /// Must be VACUUM_PUMP_1. Only one type available for now. /// - public async Task PullAirVacuumPump(RobotTool vacuumPump) - { - await SendCommandAsync(nameof(PullAirVacuumPump), vacuumPump.ToString()); - await ReceiveAnswerAsync(nameof(PullAirVacuumPump)); + public async Task PullAirVacuumPump (RobotTool vacuumPump) { + await SendCommandAsync (nameof (PullAirVacuumPump), vacuumPump.ToString ()); + await ReceiveAnswerAsync (nameof (PullAirVacuumPump)); } /// /// Push air using the vacuum pump. /// Must be VACUUM_PUMP_1. Only one type available for now. /// - public async Task PushAirVacuumPump(RobotTool vacuumPump) - { - await SendCommandAsync(nameof(PushAirVacuumPump), vacuumPump.ToString()); - await ReceiveAnswerAsync(nameof(PushAirVacuumPump)); + public async Task PushAirVacuumPump (RobotTool vacuumPump) { + await SendCommandAsync (nameof (PushAirVacuumPump), vacuumPump.ToString ()); + await ReceiveAnswerAsync (nameof (PushAirVacuumPump)); } /// @@ -217,10 +217,9 @@ public async Task PushAirVacuumPump(RobotTool vacuumPump) /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) - { - await SendCommandAsync(nameof(SetupElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(SetupElectromagnet)); + public async Task SetupElectromagnet (RobotTool tool, RobotPin pin) { + await SendCommandAsync (nameof (SetupElectromagnet), tool.ToString (), pin.ToString ()); + await ReceiveAnswerAsync (nameof (SetupElectromagnet)); } /// @@ -228,10 +227,9 @@ public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) - { - await SendCommandAsync(nameof(ActivateElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(ActivateElectromagnet)); + public async Task ActivateElectromagnet (RobotTool tool, RobotPin pin) { + await SendCommandAsync (nameof (ActivateElectromagnet), tool.ToString (), pin.ToString ()); + await ReceiveAnswerAsync (nameof (ActivateElectromagnet)); } /// @@ -239,65 +237,58 @@ public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) - { - await SendCommandAsync(nameof(DeactivateElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(DeactivateElectromagnet)); + public async Task DeactivateElectromagnet (RobotTool tool, RobotPin pin) { + await SendCommandAsync (nameof (DeactivateElectromagnet), tool.ToString (), pin.ToString ()); + await ReceiveAnswerAsync (nameof (DeactivateElectromagnet)); } /// /// Get the current joint configuration. /// - public async Task GetJoints() - { - await SendCommandAsync(nameof(GetJoints)); - var joints = await ReceiveAnswerAsync(nameof(GetJoints)); - return RobotJoints.Parse(joints); + public async Task GetJoints () { + await SendCommandAsync (nameof (GetJoints)); + var joints = await ReceiveAnswerAsync (nameof (GetJoints)); + return RobotJoints.Parse (joints); } /// /// Get the current pose. /// - public async Task GetPose() - { - await SendCommandAsync(nameof(GetPose)); - var pose = await ReceiveAnswerAsync(nameof(GetPose)); - return PoseObject.Parse(pose); + public async Task GetPose () { + await SendCommandAsync (nameof (GetPose)); + var pose = await ReceiveAnswerAsync (nameof (GetPose)); + return PoseObject.Parse (pose); } /// /// Get the current hardware status. /// - public async Task GetHardwareStatus() - { - await SendCommandAsync(nameof(GetHardwareStatus)); - var status = await ReceiveAnswerAsync(nameof(GetHardwareStatus)); - return HardwareStatus.Parse(status); + public async Task GetHardwareStatus () { + await SendCommandAsync (nameof (GetHardwareStatus)); + var status = await ReceiveAnswerAsync (nameof (GetHardwareStatus)); + return HardwareStatus.Parse (status); } /// /// Get whether the robot is in learning mode. /// - public async Task GetLearningMode() - { - await SendCommandAsync(nameof(GetLearningMode)); - var mode = await ReceiveAnswerAsync(nameof(GetLearningMode)); - return bool.Parse(mode); + public async Task GetLearningMode () { + await SendCommandAsync (nameof (GetLearningMode)); + var mode = await ReceiveAnswerAsync (nameof (GetLearningMode)); + return bool.Parse (mode); } /// /// Get the current state of the digital io pins. /// - public async Task GetDigitalIoState() - { - await SendCommandAsync(nameof(GetDigitalIoState)); - var state = await ReceiveAnswerAsync(nameof(GetDigitalIoState)); + public async Task GetDigitalIoState () { + await SendCommandAsync (nameof (GetDigitalIoState)); + var state = await ReceiveAnswerAsync (nameof (GetDigitalIoState)); - var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); - var matches = regex.Matches(state); + var regex = new Regex ("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); + var matches = regex.Matches (state); - return matches.Select(m => DigitalPinObject.Parse(m.Value)).ToArray(); + return matches.Select (m => DigitalPinObject.Parse (m.Value)).ToArray (); } - } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs index 617555e2..770e8c52 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs @@ -1,11 +1,31 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System; -namespace NiryoOneClient -{ - public class NiryoOneException : Exception - { - public NiryoOneException(string reason) - { +namespace NiryoOneClient { + public class NiryoOneException : Exception { + public NiryoOneException (string reason) { Reason = reason; } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs index 48afe8bb..b1188c02 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -1,30 +1,48 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System; using System.Collections; using System.Collections.Generic; using System.Linq; -namespace NiryoOneClient -{ - public class PoseObject : IEnumerable - { +namespace NiryoOneClient { + public class PoseObject : IEnumerable { private float[] _j = new float[6]; - public PoseObject(float x, float y, float z, float roll, float pitch, float yaw) - { - _j = new[] { x, y, z, roll, pitch, yaw }; + public PoseObject (float x, float y, float z, float roll, float pitch, float yaw) { + _j = new [] { x, y, z, roll, pitch, yaw }; } - public PoseObject(float[] j) - { + public PoseObject (float[] j) { if (j.Length != 6) - throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); + throw new ArgumentException ("Joints must be constructed from 6 values.", nameof (j)); _j = j; } - public static PoseObject Parse(string s) - { - return new PoseObject(s.Split(",").Select(float.Parse).ToArray()); + public static PoseObject Parse (string s) { + return new PoseObject (s.Split (",").Select (float.Parse).ToArray ()); } public float X { get => _j[0]; set => _j[0] = value; } @@ -34,18 +52,21 @@ public static PoseObject Parse(string s) public float Pitch { get => _j[4]; set => _j[4] = value; } public float Yaw { get => _j[5]; set => _j[5] = value; } - public IEnumerator GetEnumerator() - { - return ((IEnumerable)_j).GetEnumerator(); + public IEnumerator GetEnumerator () { + return ((IEnumerable) _j).GetEnumerator (); } - IEnumerator IEnumerable.GetEnumerator() - { - return _j.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator () { + return _j.GetEnumerator (); } } public enum RobotAxis { - X, Y, Z, ROLL, PITCH, YAW + X, + Y, + Z, + ROLL, + PITCH, + YAW } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs index c5e9e481..60af2782 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -1,30 +1,48 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System; using System.Collections; using System.Collections.Generic; using System.Linq; -namespace NiryoOneClient -{ - public class RobotJoints : IEnumerable - { +namespace NiryoOneClient { + public class RobotJoints : IEnumerable { private float[] _j = new float[6]; - public RobotJoints(float j1, float j2, float j3, float j4, float j5, float j6) - { - _j = new[] { j1, j2, j3, j4, j5, j6 }; + public RobotJoints (float j1, float j2, float j3, float j4, float j5, float j6) { + _j = new [] { j1, j2, j3, j4, j5, j6 }; } - public RobotJoints(float[] j) - { + public RobotJoints (float[] j) { if (j.Length != 6) - throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); + throw new ArgumentException ("Joints must be constructed from 6 values.", nameof (j)); _j = j; } - public static RobotJoints Parse(string s) - { - return new RobotJoints(s.Split(",").Select(float.Parse).ToArray()); + public static RobotJoints Parse (string s) { + return new RobotJoints (s.Split (",").Select (float.Parse).ToArray ()); } public float J1 { get => _j[0]; set => _j[0] = value; } @@ -34,14 +52,12 @@ public static RobotJoints Parse(string s) public float J5 { get => _j[4]; set => _j[4] = value; } public float J6 { get => _j[5]; set => _j[5] = value; } - public IEnumerator GetEnumerator() - { - return ((IEnumerable)_j).GetEnumerator(); + public IEnumerator GetEnumerator () { + return ((IEnumerable) _j).GetEnumerator (); } - IEnumerator IEnumerable.GetEnumerator() - { - return _j.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator () { + return _j.GetEnumerator (); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs index e35225a2..4a30590a 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -1,44 +1,65 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + using System; using System.Collections; using System.Collections.Generic; -namespace NiryoOneClient -{ - public enum RobotPin - { - GPIO_1A, GPIO_1B, GPIO_1C, GPIO_2A, GPIO_2B, GPIO_2C +namespace NiryoOneClient { + public enum RobotPin { + GPIO_1A, + GPIO_1B, + GPIO_1C, + GPIO_2A, + GPIO_2B, + GPIO_2C } - public enum PinMode - { + public enum PinMode { OUTPUT = 0, INPUT = 1 } - public enum DigitalState - { + public enum DigitalState { LOW = 0, HIGH = 1 } - public class DigitalPinObject - { + public class DigitalPinObject { public int PinId; public string Name; public PinMode Mode; public DigitalState State; - public static DigitalPinObject Parse(string s) - { - if (!s.StartsWith('[') || !s.EndsWith(']')) - throw new ArgumentException(); - - var ss = s.Substring(1, s.Length - 2).Split(", "); - - return new DigitalPinObject - { - PinId = int.Parse(ss[0]), - Name = ss[1].Trim().Substring(1, ss[1].Length - 2), - Mode = (PinMode)int.Parse(ss[2]), - State = (DigitalState)int.Parse(ss[3]) + public static DigitalPinObject Parse (string s) { + if (!s.StartsWith ('[') || !s.EndsWith (']')) + throw new ArgumentException (); + + var ss = s.Substring (1, s.Length - 2).Split (", "); + + return new DigitalPinObject { + PinId = int.Parse (ss[0]), + Name = ss[1].Trim ().Substring (1, ss[1].Length - 2), + Mode = (PinMode) int.Parse (ss[2]), + State = (DigitalState) int.Parse (ss[3]) }; } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs new file mode 100644 index 00000000..8bb16ca6 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs @@ -0,0 +1,36 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace NiryoOneClient { + public enum RobotTool { + GRIPPER_1, + GRIPPER_2, + GRIPPER_3, + VACUUM_PUMP_1, + ELECTROMAGNET_1 + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs deleted file mode 100644 index 11772996..00000000 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Tools.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace NiryoOneClient -{ - public enum RobotTool - { - GRIPPER_1, GRIPPER_2, GRIPPER_3, VACUUM_PUMP_1, ELECTROMAGNET_1 - } -} \ No newline at end of file From 91aae983c7598401ad3a23a59c687d99bcf968a0 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 15:47:56 +0100 Subject: [PATCH 09/38] Add pack task for creating nuget packages --- .../clients/csharp/.vscode/tasks.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json index 98ed2477..1f894496 100644 --- a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json +++ b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json @@ -17,6 +17,22 @@ "isDefault": true } }, + { + "label": "pack", + "command": "dotnet", + "type": "process", + "args": [ + "pack", + "${workspaceFolder}/NiryoOneClient.sln", + "--configuration", "Release", + "--include-symbols" + ], + "problemMatcher": "$msCompile", + "group": { + "kind": "build", + "isDefault": true + } + }, { "label": "publish", "command": "dotnet", From c2edcefd83c9fa134f4b40a1c328038ed228e6fb Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 15:48:15 +0100 Subject: [PATCH 10/38] Allow closing the client --- .../csharp/NiryoOneClient/NiryoOneClient.cs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index 25c8c59a..aecb694c 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -25,33 +25,51 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Net.Sockets; using System.Threading.Tasks; -namespace NiryoOneClient { - public enum CalibrateMode { +namespace NiryoOneClient +{ + public enum CalibrateMode + { AUTO, MANUAL } - public class NiryoOneClient { + public class NiryoOneClient : IDisposable + { private TcpClient _client; private int _port; private string _server; + private NetworkStream _stream; + private NiryoOneConnection _connection; - public NiryoOneClient (string server, int port) { + public NiryoOneClient(string server, int port) + { _server = server; _port = port; } - public async Task Connect () { - if (_client != null) { - _client.Close (); - _client.Dispose (); + public async Task Connect() + { + if (_client != null) + { + _client.Close(); _client = null; } - _client = new TcpClient (); - await _client.ConnectAsync (_server, _port); - var stream = _client.GetStream (); - return new NiryoOneConnection (new System.IO.StreamReader (stream), new System.IO.StreamWriter (stream)); + _client = new TcpClient(); + await _client.ConnectAsync(_server, _port); + _stream = _client.GetStream(); + _connection = new NiryoOneConnection(new System.IO.StreamReader(_stream), new System.IO.StreamWriter(_stream)); + return _connection; + } + + public void Dispose() + { + _stream.Close(); + _stream.Dispose(); + _stream = null; + _client.Close(); + _client.Dispose(); + _client = null; } } } \ No newline at end of file From 1d892e25ab78742fb2d3cee0ddb2c0ceaa0395bb Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 18:47:20 +0100 Subject: [PATCH 11/38] Added example, actually working --- .../clients/csharp/.vscode/tasks.json | 2 +- .../clients/csharp/Examples/Examples.csproj | 12 + .../clients/csharp/Examples/Program.cs | 44 +++ .../HardwareStatusTest.cs | 33 +- .../NiryoOneConnectionTest.cs | 373 ++++++++++-------- .../clients/csharp/NiryoOneClient.sln | 14 + .../csharp/NiryoOneClient/HardwareStatus.cs | 70 ++-- .../csharp/NiryoOneClient/NiryoOneClient.cs | 2 +- .../NiryoOneClient/NiryoOneConnection.cs | 230 ++++++----- .../NiryoOneClient/NiryoOneException.cs | 9 +- .../csharp/NiryoOneClient/PoseObject.cs | 34 +- .../csharp/NiryoOneClient/RobotJoints.cs | 31 +- .../clients/csharp/NiryoOneClient/RobotPin.cs | 35 +- .../csharp/NiryoOneClient/RobotTool.cs | 6 +- 14 files changed, 534 insertions(+), 361 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj create mode 100644 niryo_one_tcp_server/clients/csharp/Examples/Program.cs diff --git a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json index 1f894496..1f945400 100644 --- a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json +++ b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json @@ -30,7 +30,7 @@ "problemMatcher": "$msCompile", "group": { "kind": "build", - "isDefault": true + "isDefault": false } }, { diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj new file mode 100644 index 00000000..20006a0a --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj @@ -0,0 +1,12 @@ + + + + + + + + Exe + netcoreapp3.0 + + + diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs new file mode 100644 index 00000000..4bed8655 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using NiryoOneClient; + +namespace Examples +{ + class Program + { + public static async Task Main(string[] args) + { + string server = args.FirstOrDefault() ?? "10.10.10.10"; + + using (var niryoOneClient = new NiryoOneClient.NiryoOneClient(server)) + { + Console.WriteLine($"Connecting to {server}:40001"); + var niryo = await niryoOneClient.Connect(); + Console.WriteLine($"Connected!"); + + PoseObject initialPose = null; + + Console.WriteLine("Calibrating..."); + await niryo.Calibrate(CalibrateMode.AUTO); + Console.WriteLine("Done!"); + + var pose = await niryo.GetPose(); + await niryo.MoveJoints(new RobotJoints(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + await niryo.ShiftPose(RobotAxis.Y, 0.15f); + + if (initialPose != null) + { + await niryo.MovePose(initialPose); + } + + var digitalIoPins = await niryo.GetDigitalIoState(); + + foreach (var pin in digitalIoPins) + Console.WriteLine($"Pin: {pin.PinId}, name: {pin.Name}, mode: {pin.Mode}, state: {pin.State}"); + + await niryo.SetLearningMode(true); + } + } + } +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs index 4609c14b..8ba82ded 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs @@ -23,27 +23,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace NiryoOneClient.Tests { +namespace NiryoOneClient.Tests +{ [TestClass] - public class HardwareStatusTest { + public class HardwareStatusTest + { [TestMethod] - public void Parse_Sample_Works () { + public void Parse_Sample_Works() + { // Arrange var s = "[59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)]"; // Act - var hs = HardwareStatus.Parse (s); + var hs = HardwareStatus.Parse(s); // Assert - Assert.AreEqual (59, hs.RpiTemperature); - Assert.AreEqual (2, hs.HardwareVersion); - Assert.AreEqual (true, hs.ConnectionUp); - Assert.AreEqual ("", hs.ErrorMessage); - Assert.AreEqual (0, hs.CalibrationNeeded); - Assert.AreEqual (false, hs.CalibrationInProgress); - CollectionAssert.AreEqual (new [] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, hs.MotorNames); - CollectionAssert.AreEqual (new [] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, hs.MotorTypes); - CollectionAssert.AreEqual (new [] { 34, 34, 37, 43, 45, 37 }, hs.Temperatures); - CollectionAssert.AreEqual (new [] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, hs.Voltages); - CollectionAssert.AreEqual (new [] { 0, 0, 0, 0, 0, 0 }, hs.HardwareErrors); + Assert.AreEqual(59, hs.RpiTemperature); + Assert.AreEqual(2, hs.HardwareVersion); + Assert.AreEqual(true, hs.ConnectionUp); + Assert.AreEqual("", hs.ErrorMessage); + Assert.AreEqual(0, hs.CalibrationNeeded); + Assert.AreEqual(false, hs.CalibrationInProgress); + CollectionAssert.AreEqual(new[] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, hs.MotorNames); + CollectionAssert.AreEqual(new[] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, hs.MotorTypes); + CollectionAssert.AreEqual(new[] { 34, 34, 37, 43, 45, 37 }, hs.Temperatures); + CollectionAssert.AreEqual(new[] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, hs.Voltages); + CollectionAssert.AreEqual(new[] { 0, 0, 0, 0, 0, 0 }, hs.HardwareErrors); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index fd54b9a9..8ecef0fa 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -27,299 +27,330 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; -namespace NiryoOneClient.Tests { +namespace NiryoOneClient.Tests +{ [TestClass] - public class NiryoOneConnectionTest { + public class NiryoOneConnectionTest + { private TextReader _streamReader; private TextWriter _streamWriter; private NiryoOneConnection _connection; - public NiryoOneConnectionTest () { - _streamReader = Substitute.For (); - _streamWriter = Substitute.For (); - _connection = new NiryoOneConnection (_streamReader, _streamWriter); + public NiryoOneConnectionTest() + { + _streamReader = Substitute.For(); + _streamWriter = Substitute.For(); + _connection = new NiryoOneConnection(_streamReader, _streamWriter); } [TestMethod] - public async Task Calibrate_SuccessfulAuto_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:OK\n")); - await _connection.Calibrate (CalibrateMode.AUTO); - await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:AUTO"); + public async Task Calibrate_SuccessfulAuto_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + await _connection.Calibrate(CalibrateMode.AUTO); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); } [TestMethod] - public async Task Calibrate_SuccessfulManual_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:OK\n")); - await _connection.Calibrate (CalibrateMode.MANUAL); - await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:MANUAL"); + public async Task Calibrate_SuccessfulManual_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + await _connection.Calibrate(CalibrateMode.MANUAL); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); } [TestMethod] - public async Task Calibrate_Failure_Throws () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("CALIBRATE:KO,\"Sucks to be sucky\"")); - var e = await Assert.ThrowsExceptionAsync (async () => await _connection.Calibrate (CalibrateMode.MANUAL)); - Assert.AreEqual ("Sucks to be sucky", e.Reason); - await _streamWriter.Received ().WriteLineAsync ("CALIBRATE:MANUAL"); + public async Task Calibrate_Failure_Throws() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); + Assert.AreEqual("Sucks to be sucky", e.Reason); + await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); } [TestMethod] - public async Task SetLearningMode_True_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:OK")); - await _connection.SetLearningMode (true); - await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:TRUE"); + public async Task SetLearningMode_True_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode(true); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } [TestMethod] - public async Task SetLearningMode_False_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:OK")); - await _connection.SetLearningMode (false); - await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_False_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + await _connection.SetLearningMode(false); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task SetLearningMode_WrongResponse_Throws () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("COCO:OK")); - var e = await Assert.ThrowsExceptionAsync (async () => await _connection.SetLearningMode (false)); - Assert.AreEqual ("Wrong command response received.", e.Reason); - await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_WrongResponse_Throws() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("COCO:OK")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); + Assert.AreEqual("Wrong command response received.", e.Reason); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task SetLearningMode_ErrorResponse_Throws () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_LEARNING_MODE:KO,\"No good\"")); - var e = await Assert.ThrowsExceptionAsync (async () => await _connection.SetLearningMode (false)); - Assert.AreEqual ("No good", e.Reason); - await _streamWriter.Received ().WriteLineAsync ("SET_LEARNING_MODE:FALSE"); + public async Task SetLearningMode_ErrorResponse_Throws() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:KO,\"No good\"")); + var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); + Assert.AreEqual("No good", e.Reason); + await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } [TestMethod] - public async Task MoveJoints_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("MOVE_JOINTS:OK")); - await _connection.MoveJoints (new RobotJoints (new [] { + public async Task MoveJoints_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_JOINTS:OK")); + await _connection.MoveJoints(new RobotJoints(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); - await _streamWriter.Received ().WriteLineAsync ("MOVE_JOINTS:0.03,0.0123,0.456,0.987,0.654,0.321"); + await _streamWriter.Received().WriteLineAsync("MOVE_JOINTS:0.03,0.0123,0.456,0.987,0.654,0.321"); } [TestMethod] - public async Task MovePose_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("MOVE_POSE:OK")); - await _connection.MovePose (new PoseObject (new [] { + public async Task MovePose_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_POSE:OK")); + await _connection.MovePose(new PoseObject(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); - await _streamWriter.Received ().WriteLineAsync ("MOVE_POSE:0.03,0.0123,0.456,0.987,0.654,0.321"); + await _streamWriter.Received().WriteLineAsync("MOVE_POSE:0.03,0.0123,0.456,0.987,0.654,0.321"); } [TestMethod] - public async Task ShiftPose_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SHIFT_POSE:OK")); - await _connection.ShiftPose (RobotAxis.ROLL, 0.03142f); - await _streamWriter.Received ().WriteLineAsync ("SHIFT_POSE:ROLL,0.03142"); + public async Task ShiftPose_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SHIFT_POSE:OK")); + await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); + await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); } [TestMethod] - public async Task SetArmMaxVelocity_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_ARM_MAX_VELOCITY:OK")); - await _connection.SetArmMaxVelocity (50); - await _streamWriter.Received ().WriteLineAsync ("SET_ARM_MAX_VELOCITY:50"); + public async Task SetArmMaxVelocity_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_ARM_MAX_VELOCITY:OK")); + await _connection.SetArmMaxVelocity(50); + await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); } [TestMethod] - public async Task EnableJoystick_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("ENABLE_JOYSTICK:OK")); - await _connection.EnableJoystick (false); - await _streamWriter.Received ().WriteLineAsync ("ENABLE_JOYSTICK:FALSE"); + public async Task EnableJoystick_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("ENABLE_JOYSTICK:OK")); + await _connection.EnableJoystick(false); + await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); } [TestMethod] - public async Task SetPinMode_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("SET_PIN_MODE:OK")); - await _connection.SetPinMode (RobotPin.GPIO_2B, PinMode.OUTPUT); - await _streamWriter.Received ().WriteLineAsync ("SET_PIN_MODE:GPIO_2B,OUTPUT"); + public async Task SetPinMode_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_PIN_MODE:OK")); + await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); + await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); } [TestMethod] - public async Task DigitalWrite_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task DigitalWrite_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "DIGITAL_WRITE:OK" )); - await _connection.DigitalWrite (RobotPin.GPIO_2A, DigitalState.LOW); - await _streamWriter.Received ().WriteLineAsync ("DIGITAL_WRITE:GPIO_2A,LOW"); + await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); + await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); } [TestMethod] - public async Task DigitalRead_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task DigitalRead_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "DIGITAL_READ:OK,HIGH" )); - await _connection.DigitalRead (RobotPin.GPIO_1A); - await _streamWriter.Received ().WriteLineAsync ("DIGITAL_READ:GPIO_1A"); + await _connection.DigitalRead(RobotPin.GPIO_1A); + await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); } [TestMethod] - public async Task ChangeTool_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task ChangeTool_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "CHANGE_TOOL:OK" )); - await _connection.ChangeTool (RobotTool.GRIPPER_2); - await _streamWriter.Received ().WriteLineAsync ("CHANGE_TOOL:GRIPPER_2"); + await _connection.ChangeTool(RobotTool.GRIPPER_2); + await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); } [TestMethod] - public async Task OpenGripper_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task OpenGripper_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "OPEN_GRIPPER:OK" )); - await _connection.OpenGripper (RobotTool.GRIPPER_1, 200); - await _streamWriter.Received ().WriteLineAsync ("OPEN_GRIPPER:GRIPPER_1,200"); + await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); + await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); } [TestMethod] - public async Task CloseGripper_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task CloseGripper_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "CLOSE_GRIPPER:OK" )); - await _connection.CloseGripper (RobotTool.GRIPPER_1, 200); - await _streamWriter.Received ().WriteLineAsync ("CLOSE_GRIPPER:GRIPPER_1,200"); + await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); + await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); } [TestMethod] - public async Task PullAirVacuumPump_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task PullAirVacuumPump_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "PULL_AIR_VACUUM_PUMP:OK" )); - await _connection.PullAirVacuumPump (RobotTool.VACUUM_PUMP_1); - await _streamWriter.Received ().WriteLineAsync ("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } [TestMethod] - public async Task PushAirVacuumPump_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task PushAirVacuumPump_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "PUSH_AIR_VACUUM_PUMP:OK" )); - await _connection.PushAirVacuumPump (RobotTool.VACUUM_PUMP_1); - await _streamWriter.Received ().WriteLineAsync ("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); + await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); + await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } [TestMethod] - public async Task SetupElectromagnet_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task SetupElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "SETUP_ELECTROMAGNET:OK" )); - await _connection.SetupElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); - await _streamWriter.Received ().WriteLineAsync ("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } [TestMethod] - public async Task ActivateElectromagnet_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task ActivateElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "ACTIVATE_ELECTROMAGNET:OK" )); - await _connection.ActivateElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); - await _streamWriter.Received ().WriteLineAsync ("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); + await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); + await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } [TestMethod] - public async Task DeactivateElectromagnet_Sample_SendsCorrectly () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task DeactivateElectromagnet_Sample_SendsCorrectly() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "DEACTIVATE_ELECTROMAGNET:OK" )); - await _connection.DeactivateElectromagnet (RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); - await _streamWriter.Received ().WriteLineAsync ("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); + await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); + await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); } [TestMethod] - public async Task GetJoints_Sample_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task GetJoints_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0" )); - var joints = await _connection.GetJoints (); - CollectionAssert.AreEqual (new [] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray ()); + var joints = await _connection.GetJoints(); + CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); } [TestMethod] - public async Task GetPose_Sample_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task GetPose_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12" )); - var pose = await _connection.GetPose (); - CollectionAssert.AreEqual (new [] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, - pose.ToArray ()); + var pose = await _connection.GetPose(); + CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, + pose.ToArray()); } [TestMethod] - public async Task GetHardwareStatus_Sample_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task GetHardwareStatus_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)" )); - var status = await _connection.GetHardwareStatus (); - Assert.AreEqual (59, status.RpiTemperature); - Assert.AreEqual (2, status.HardwareVersion); - Assert.AreEqual (true, status.ConnectionUp); - Assert.AreEqual ("", status.ErrorMessage); - Assert.AreEqual (0, status.CalibrationNeeded); - Assert.AreEqual (false, status.CalibrationInProgress); - CollectionAssert.AreEqual (new [] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, status.MotorNames); - CollectionAssert.AreEqual (new [] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, status.MotorTypes); - CollectionAssert.AreEqual (new [] { 34, 34, 37, 43, 45, 37 }, status.Temperatures); - CollectionAssert.AreEqual (new [] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, status.Voltages); - CollectionAssert.AreEqual (new [] { 0, 0, 0, 0, 0, 0 }, status.HardwareErrors); + var status = await _connection.GetHardwareStatus(); + Assert.AreEqual(59, status.RpiTemperature); + Assert.AreEqual(2, status.HardwareVersion); + Assert.AreEqual(true, status.ConnectionUp); + Assert.AreEqual("", status.ErrorMessage); + Assert.AreEqual(0, status.CalibrationNeeded); + Assert.AreEqual(false, status.CalibrationInProgress); + CollectionAssert.AreEqual(new[] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, status.MotorNames); + CollectionAssert.AreEqual(new[] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, status.MotorTypes); + CollectionAssert.AreEqual(new[] { 34, 34, 37, 43, 45, 37 }, status.Temperatures); + CollectionAssert.AreEqual(new[] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, status.Voltages); + CollectionAssert.AreEqual(new[] { 0, 0, 0, 0, 0, 0 }, status.HardwareErrors); } [TestMethod] - public async Task GetLearningMode_Sample_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ("GET_LEARNING_MODE:OK,FALSE")); - var mode = await _connection.GetLearningMode (); - await _streamWriter.Received ().WriteLineAsync ("GET_LEARNING_MODE"); - Assert.AreEqual (false, mode); + public async Task GetLearningMode_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult("GET_LEARNING_MODE:OK,FALSE")); + var mode = await _connection.GetLearningMode(); + await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); + Assert.AreEqual(false, mode); } [TestMethod] - public async Task GetDigitalIoState_Sample_Works () { - _streamReader.ReadLineAsync ().Returns (Task.FromResult ( + public async Task GetDigitalIoState_Sample_Works() + { + _streamReader.ReadLineAsync().Returns(Task.FromResult( "GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]" )); - var state = await _connection.GetDigitalIoState (); - Assert.AreEqual (2, state[0].PinId); - Assert.AreEqual ("1A", state[0].Name); - Assert.AreEqual (PinMode.INPUT, state[0].Mode); - Assert.AreEqual (DigitalState.HIGH, state[0].State); - - Assert.AreEqual (3, state[1].PinId); - Assert.AreEqual ("1B", state[1].Name); - Assert.AreEqual (PinMode.INPUT, state[1].Mode); - Assert.AreEqual (DigitalState.HIGH, state[1].State); - - Assert.AreEqual (16, state[2].PinId); - Assert.AreEqual ("1C", state[2].Name); - Assert.AreEqual (PinMode.INPUT, state[2].Mode); - Assert.AreEqual (DigitalState.HIGH, state[2].State); - - Assert.AreEqual (26, state[3].PinId); - Assert.AreEqual ("2A", state[3].Name); - Assert.AreEqual (PinMode.INPUT, state[3].Mode); - Assert.AreEqual (DigitalState.HIGH, state[3].State); - - Assert.AreEqual (19, state[4].PinId); - Assert.AreEqual ("2B", state[4].Name); - Assert.AreEqual (PinMode.INPUT, state[4].Mode); - Assert.AreEqual (DigitalState.HIGH, state[4].State); - - Assert.AreEqual (6, state[5].PinId); - Assert.AreEqual ("2C", state[5].Name); - Assert.AreEqual (PinMode.INPUT, state[5].Mode); - Assert.AreEqual (DigitalState.HIGH, state[5].State); - - Assert.AreEqual (12, state[6].PinId); - Assert.AreEqual ("SW1", state[6].Name); - Assert.AreEqual (PinMode.OUTPUT, state[6].Mode); - Assert.AreEqual (DigitalState.LOW, state[6].State); - - Assert.AreEqual (13, state[7].PinId); - Assert.AreEqual ("SW2", state[7].Name); - Assert.AreEqual (PinMode.OUTPUT, state[7].Mode); - Assert.AreEqual (DigitalState.LOW, state[7].State); + var state = await _connection.GetDigitalIoState(); + Assert.AreEqual(2, state[0].PinId); + Assert.AreEqual("1A", state[0].Name); + Assert.AreEqual(PinMode.INPUT, state[0].Mode); + Assert.AreEqual(DigitalState.HIGH, state[0].State); + + Assert.AreEqual(3, state[1].PinId); + Assert.AreEqual("1B", state[1].Name); + Assert.AreEqual(PinMode.INPUT, state[1].Mode); + Assert.AreEqual(DigitalState.HIGH, state[1].State); + + Assert.AreEqual(16, state[2].PinId); + Assert.AreEqual("1C", state[2].Name); + Assert.AreEqual(PinMode.INPUT, state[2].Mode); + Assert.AreEqual(DigitalState.HIGH, state[2].State); + + Assert.AreEqual(26, state[3].PinId); + Assert.AreEqual("2A", state[3].Name); + Assert.AreEqual(PinMode.INPUT, state[3].Mode); + Assert.AreEqual(DigitalState.HIGH, state[3].State); + + Assert.AreEqual(19, state[4].PinId); + Assert.AreEqual("2B", state[4].Name); + Assert.AreEqual(PinMode.INPUT, state[4].Mode); + Assert.AreEqual(DigitalState.HIGH, state[4].State); + + Assert.AreEqual(6, state[5].PinId); + Assert.AreEqual("2C", state[5].Name); + Assert.AreEqual(PinMode.INPUT, state[5].Mode); + Assert.AreEqual(DigitalState.HIGH, state[5].State); + + Assert.AreEqual(12, state[6].PinId); + Assert.AreEqual("SW1", state[6].Name); + Assert.AreEqual(PinMode.OUTPUT, state[6].Mode); + Assert.AreEqual(DigitalState.LOW, state[6].State); + + Assert.AreEqual(13, state[7].PinId); + Assert.AreEqual("SW2", state[7].Name); + Assert.AreEqual(PinMode.OUTPUT, state[7].Mode); + Assert.AreEqual(DigitalState.LOW, state[7].State); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln index 3db09dfb..ae46dd4c 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NiryoOneClient", "NiryoOneC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NiryoOneClient.Tests", "NiryoOneClient.Tests\NiryoOneClient.Tests.csproj", "{41BDE194-283F-47F8-98A4-978A65393FFC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{7D509E8D-D71B-45FA-B510-526C8ED229D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,5 +46,17 @@ Global {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x64.Build.0 = Release|Any CPU {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x86.ActiveCfg = Release|Any CPU {41BDE194-283F-47F8-98A4-978A65393FFC}.Release|x86.Build.0 = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|x64.Build.0 = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Debug|x86.Build.0 = Debug|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|Any CPU.Build.0 = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|x64.ActiveCfg = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|x64.Build.0 = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|x86.ActiveCfg = Release|Any CPU + {7D509E8D-D71B-45FA-B510-526C8ED229D1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index bac74bf7..b8beb9e2 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -27,49 +27,57 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Linq; using System.Text.Json; -namespace NiryoOneClient { - public class HardwareStatus { - private static string Strip_ (string s, char prefix, char suffix) { - if (!s.StartsWith (prefix)) - throw new ArgumentException (); - if (!s.EndsWith (suffix)) - throw new ArgumentException (); - return s.Substring (1, s.Length - 2); +namespace NiryoOneClient +{ + public class HardwareStatus + { + private static string Strip_(string s, char prefix, char suffix) + { + if (!s.StartsWith(prefix)) + throw new ArgumentException(); + if (!s.EndsWith(suffix)) + throw new ArgumentException(); + return s.Substring(1, s.Length - 2); } - private static string[] ParseStrings_ (string s) { - var regex = new System.Text.RegularExpressions.Regex (@"'[^']*'"); - return regex.Matches (s).Select (m => Strip_ (m.Value, '\'', '\'')).ToArray (); + private static string[] ParseStrings_(string s) + { + var regex = new System.Text.RegularExpressions.Regex(@"'[^']*'"); + return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); } - private static T[] ParseNumbers_ (string s, Func parser) { - var regex = new System.Text.RegularExpressions.Regex (@"[0-9]+(\.[0-9]*)?"); - return regex.Matches (s).Select (m => parser (m.Value)).ToArray (); + private static T[] ParseNumbers_(string s, Func parser) + { + var regex = new System.Text.RegularExpressions.Regex(@"[0-9]+(\.[0-9]*)?"); + return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); } - public static HardwareStatus Parse (string data) { - var regex = new System.Text.RegularExpressions.Regex (@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); - var matches = regex.Matches (data); + public static HardwareStatus Parse(string data) + { + var regex = new System.Text.RegularExpressions.Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); + var matches = regex.Matches(data); - if (matches.Count != 11) { - throw new NiryoOneException ("Incorrect answer received, cannot understand received format."); + if (matches.Count != 11) + { + throw new NiryoOneException("Incorrect answer received, cannot understand received format."); } - var rpiTemperature = int.Parse (matches[0].Value); - var hardwareVersion = int.Parse (matches[1].Value); - var connectionUp = bool.Parse (matches[2].Value); - var errorMessage = Strip_ (matches[3].Value, '\'', '\''); - var calibrationNeeded = int.Parse (matches[4].Value); - var calibrationInProgress = bool.Parse (matches[5].Value); + var rpiTemperature = int.Parse(matches[0].Value); + var hardwareVersion = int.Parse(matches[1].Value); + var connectionUp = bool.Parse(matches[2].Value); + var errorMessage = Strip_(matches[3].Value, '\'', '\''); + var calibrationNeeded = int.Parse(matches[4].Value); + var calibrationInProgress = bool.Parse(matches[5].Value); - var motorNames = ParseStrings_ (matches[6].Value); - var motorTypes = ParseStrings_ (matches[7].Value); + var motorNames = ParseStrings_(matches[6].Value); + var motorTypes = ParseStrings_(matches[7].Value); - var temperatures = ParseNumbers_ (matches[8].Value, int.Parse); - var voltages = ParseNumbers_ (matches[9].Value, decimal.Parse); - var hardwareErrors = ParseNumbers_ (matches[10].Value, int.Parse); + var temperatures = ParseNumbers_(matches[8].Value, int.Parse); + var voltages = ParseNumbers_(matches[9].Value, decimal.Parse); + var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); - var hardwareStatus = new HardwareStatus () { + var hardwareStatus = new HardwareStatus() + { RpiTemperature = rpiTemperature, HardwareVersion = hardwareVersion, ConnectionUp = connectionUp, diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index aecb694c..c4723a61 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -41,7 +41,7 @@ public class NiryoOneClient : IDisposable private NetworkStream _stream; private NiryoOneConnection _connection; - public NiryoOneClient(string server, int port) + public NiryoOneClient(string server, int port = 40001) { _server = server; _port = port; diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index f8a81d20..9930e4dc 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -29,48 +29,59 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace NiryoOneClient { - public class NiryoOneConnection { +namespace NiryoOneClient +{ + public class NiryoOneConnection + { private readonly TextWriter _textWriter; private readonly TextReader _textReader; - public NiryoOneConnection (TextReader streamReader, TextWriter streamWriter) { + public NiryoOneConnection(TextReader streamReader, TextWriter streamWriter) + { _textWriter = streamWriter; _textReader = streamReader; } - internal async Task WriteLineAsync (string s) { - await _textWriter.WriteLineAsync (s); + internal async Task WriteLineAsync(string s) + { + await _textWriter.WriteLineAsync(s); + await _textWriter.FlushAsync(); } - internal async Task ReadLineAsync () { - return await _textReader.ReadLineAsync (); + internal async Task ReadLineAsync() + { + return await _textReader.ReadLineAsync(); } - protected string ToSnakeCaseUpper (string s) { - return string.Concat (s.Select ((x, i) => i > 0 && char.IsUpper (x) ? "_" + x.ToString () : x.ToString ())).ToUpper (); + protected string ToSnakeCaseUpper(string s) + { + return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); } - protected async Task SendCommandAsync (string command_type, params string[] args) { + protected async Task SendCommandAsync(string command_type, params string[] args) + { string cmd; - if (args.Any ()) + if (args.Any()) cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; else - cmd = ToSnakeCaseUpper (command_type); - await WriteLineAsync (cmd); + cmd = ToSnakeCaseUpper(command_type); + Console.WriteLine(cmd); + await WriteLineAsync(cmd); + Console.WriteLine("r"); } - protected async Task ReceiveAnswerAsync (string command_type) { - var result = await ReadLineAsync (); - result = result.TrimEnd ('\n'); - var colonSplit = result.Split (':', 2); + protected async Task ReceiveAnswerAsync(string command_type) + { + var result = await ReadLineAsync(); + result = result.TrimEnd('\n'); + var colonSplit = result.Split(':', 2); var cmd = colonSplit[0]; - if (cmd != ToSnakeCaseUpper (command_type)) - throw new NiryoOneException ("Wrong command response received."); - var commaSplit2 = colonSplit[1].Split (',', 2); + if (cmd != ToSnakeCaseUpper(command_type)) + throw new NiryoOneException("Wrong command response received."); + var commaSplit2 = colonSplit[1].Split(',', 2); var status = commaSplit2[0]; if (status != "OK") - throw new NiryoOneException (commaSplit2[1].TrimStart ('"').TrimEnd ('"')); + throw new NiryoOneException(commaSplit2[1].TrimStart('"').TrimEnd('"')); if (commaSplit2.Length > 1) return commaSplit2[1]; @@ -82,36 +93,40 @@ protected async Task ReceiveAnswerAsync (string command_type) { /// Request calibration. /// Whether to request automatic or manual calibration /// - public async Task Calibrate (CalibrateMode mode) { - await SendCommandAsync (nameof (Calibrate), mode.ToString ()); - await ReceiveAnswerAsync (nameof (Calibrate)); + public async Task Calibrate(CalibrateMode mode) + { + await SendCommandAsync(nameof(Calibrate), mode.ToString()); + await ReceiveAnswerAsync(nameof(Calibrate)); } /// /// Set whether the robot should be in learning mode or not. /// Activate learning mode or not /// - public async Task SetLearningMode (bool mode) { - await SendCommandAsync (nameof (SetLearningMode), mode.ToString ().ToUpper ()); - await ReceiveAnswerAsync (nameof (SetLearningMode)); + public async Task SetLearningMode(bool mode) + { + await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); + await ReceiveAnswerAsync(nameof(SetLearningMode)); } /// /// Move joints to specified configuration. /// The desired destination joint configuration /// - public async Task MoveJoints (RobotJoints joints) { - await SendCommandAsync (nameof (MoveJoints), string.Join (',', joints)); - await ReceiveAnswerAsync (nameof (MoveJoints)); + public async Task MoveJoints(RobotJoints joints) + { + await SendCommandAsync(nameof(MoveJoints), string.Join(',', joints)); + await ReceiveAnswerAsync(nameof(MoveJoints)); } /// /// Move joints to specified pose. /// The desired destination pose /// - public async Task MovePose (PoseObject pose) { - await SendCommandAsync (nameof (MovePose), string.Join (',', pose)); - await ReceiveAnswerAsync (nameof (MovePose)); + public async Task MovePose(PoseObject pose) + { + await SendCommandAsync(nameof(MovePose), string.Join(',', pose)); + await ReceiveAnswerAsync(nameof(MovePose)); } /// @@ -119,59 +134,66 @@ public async Task MovePose (PoseObject pose) { /// Which axis to shift /// The amount to shift (meters or radians) /// - public async Task ShiftPose (RobotAxis axis, float value) { - await SendCommandAsync (nameof (ShiftPose), axis.ToString (), value.ToString ()); - await ReceiveAnswerAsync (nameof (ShiftPose)); + public async Task ShiftPose(RobotAxis axis, float value) + { + await SendCommandAsync(nameof(ShiftPose), axis.ToString(), value.ToString()); + await ReceiveAnswerAsync(nameof(ShiftPose)); } /// /// Set the maximum arm velocity.false /// The maximum velocity in percent of maximum velocity. /// - public async Task SetArmMaxVelocity (int velocity) { - await SendCommandAsync (nameof (SetArmMaxVelocity), velocity.ToString ()); - await ReceiveAnswerAsync (nameof (SetArmMaxVelocity)); + public async Task SetArmMaxVelocity(int velocity) + { + await SendCommandAsync(nameof(SetArmMaxVelocity), velocity.ToString()); + await ReceiveAnswerAsync(nameof(SetArmMaxVelocity)); } /// /// Enable or disable joystick control.false /// - public async Task EnableJoystick (bool mode) { - await SendCommandAsync (nameof (EnableJoystick), mode.ToString ().ToUpper ()); - await ReceiveAnswerAsync (nameof (EnableJoystick)); + public async Task EnableJoystick(bool mode) + { + await SendCommandAsync(nameof(EnableJoystick), mode.ToString().ToUpper()); + await ReceiveAnswerAsync(nameof(EnableJoystick)); } /// /// Configure a GPIO pin for input or output. /// - public async Task SetPinMode (RobotPin pin, PinMode mode) { - await SendCommandAsync (nameof (SetPinMode), pin.ToString (), mode.ToString ()); - await ReceiveAnswerAsync (nameof (SetPinMode)); + public async Task SetPinMode(RobotPin pin, PinMode mode) + { + await SendCommandAsync(nameof(SetPinMode), pin.ToString(), mode.ToString()); + await ReceiveAnswerAsync(nameof(SetPinMode)); } /// /// Write to a digital pin configured as output. /// - public async Task DigitalWrite (RobotPin pin, DigitalState state) { - await SendCommandAsync (nameof (DigitalWrite), pin.ToString (), state.ToString ()); - await ReceiveAnswerAsync (nameof (DigitalWrite)); + public async Task DigitalWrite(RobotPin pin, DigitalState state) + { + await SendCommandAsync(nameof(DigitalWrite), pin.ToString(), state.ToString()); + await ReceiveAnswerAsync(nameof(DigitalWrite)); } /// /// Read from a digital pin configured as input. /// - public async Task DigitalRead (RobotPin pin) { - await SendCommandAsync (nameof (DigitalRead), pin.ToString ()); - var state = await ReceiveAnswerAsync (nameof (DigitalRead)); - return (DigitalState) Enum.Parse (typeof (DigitalState), state); + public async Task DigitalRead(RobotPin pin) + { + await SendCommandAsync(nameof(DigitalRead), pin.ToString()); + var state = await ReceiveAnswerAsync(nameof(DigitalRead)); + return (DigitalState)Enum.Parse(typeof(DigitalState), state); } /// /// Select which tool is connected to the robot. /// - public async Task ChangeTool (RobotTool tool) { - await SendCommandAsync (nameof (ChangeTool), tool.ToString ()); - await ReceiveAnswerAsync (nameof (ChangeTool)); + public async Task ChangeTool(RobotTool tool) + { + await SendCommandAsync(nameof(ChangeTool), tool.ToString()); + await ReceiveAnswerAsync(nameof(ChangeTool)); } /// @@ -179,9 +201,10 @@ public async Task ChangeTool (RobotTool tool) { /// Which gripper to open /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. /// - public async Task OpenGripper (RobotTool gripper, int speed) { - await SendCommandAsync (nameof (OpenGripper), gripper.ToString (), speed.ToString ()); - await ReceiveAnswerAsync (nameof (OpenGripper)); + public async Task OpenGripper(RobotTool gripper, int speed) + { + await SendCommandAsync(nameof(OpenGripper), gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync(nameof(OpenGripper)); } /// @@ -189,27 +212,30 @@ public async Task OpenGripper (RobotTool gripper, int speed) { /// Which gripper to close /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. /// - public async Task CloseGripper (RobotTool gripper, int speed) { - await SendCommandAsync (nameof (CloseGripper), gripper.ToString (), speed.ToString ()); - await ReceiveAnswerAsync (nameof (CloseGripper)); + public async Task CloseGripper(RobotTool gripper, int speed) + { + await SendCommandAsync(nameof(CloseGripper), gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync(nameof(CloseGripper)); } /// /// Pull air using the vacuum pump. /// Must be VACUUM_PUMP_1. Only one type available for now. /// - public async Task PullAirVacuumPump (RobotTool vacuumPump) { - await SendCommandAsync (nameof (PullAirVacuumPump), vacuumPump.ToString ()); - await ReceiveAnswerAsync (nameof (PullAirVacuumPump)); + public async Task PullAirVacuumPump(RobotTool vacuumPump) + { + await SendCommandAsync(nameof(PullAirVacuumPump), vacuumPump.ToString()); + await ReceiveAnswerAsync(nameof(PullAirVacuumPump)); } /// /// Push air using the vacuum pump. /// Must be VACUUM_PUMP_1. Only one type available for now. /// - public async Task PushAirVacuumPump (RobotTool vacuumPump) { - await SendCommandAsync (nameof (PushAirVacuumPump), vacuumPump.ToString ()); - await ReceiveAnswerAsync (nameof (PushAirVacuumPump)); + public async Task PushAirVacuumPump(RobotTool vacuumPump) + { + await SendCommandAsync(nameof(PushAirVacuumPump), vacuumPump.ToString()); + await ReceiveAnswerAsync(nameof(PushAirVacuumPump)); } /// @@ -217,9 +243,10 @@ public async Task PushAirVacuumPump (RobotTool vacuumPump) { /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task SetupElectromagnet (RobotTool tool, RobotPin pin) { - await SendCommandAsync (nameof (SetupElectromagnet), tool.ToString (), pin.ToString ()); - await ReceiveAnswerAsync (nameof (SetupElectromagnet)); + public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(SetupElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(SetupElectromagnet)); } /// @@ -227,9 +254,10 @@ public async Task SetupElectromagnet (RobotTool tool, RobotPin pin) { /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task ActivateElectromagnet (RobotTool tool, RobotPin pin) { - await SendCommandAsync (nameof (ActivateElectromagnet), tool.ToString (), pin.ToString ()); - await ReceiveAnswerAsync (nameof (ActivateElectromagnet)); + public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(ActivateElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(ActivateElectromagnet)); } /// @@ -237,58 +265,64 @@ public async Task ActivateElectromagnet (RobotTool tool, RobotPin pin) { /// Must be ELECTROMAGNET_1. Only one type available for now. /// The pin to which the magnet is connected. /// - public async Task DeactivateElectromagnet (RobotTool tool, RobotPin pin) { - await SendCommandAsync (nameof (DeactivateElectromagnet), tool.ToString (), pin.ToString ()); - await ReceiveAnswerAsync (nameof (DeactivateElectromagnet)); + public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) + { + await SendCommandAsync(nameof(DeactivateElectromagnet), tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync(nameof(DeactivateElectromagnet)); } /// /// Get the current joint configuration. /// - public async Task GetJoints () { - await SendCommandAsync (nameof (GetJoints)); - var joints = await ReceiveAnswerAsync (nameof (GetJoints)); - return RobotJoints.Parse (joints); + public async Task GetJoints() + { + await SendCommandAsync(nameof(GetJoints)); + var joints = await ReceiveAnswerAsync(nameof(GetJoints)); + return RobotJoints.Parse(joints); } /// /// Get the current pose. /// - public async Task GetPose () { - await SendCommandAsync (nameof (GetPose)); - var pose = await ReceiveAnswerAsync (nameof (GetPose)); - return PoseObject.Parse (pose); + public async Task GetPose() + { + await SendCommandAsync(nameof(GetPose)); + var pose = await ReceiveAnswerAsync(nameof(GetPose)); + return PoseObject.Parse(pose); } /// /// Get the current hardware status. /// - public async Task GetHardwareStatus () { - await SendCommandAsync (nameof (GetHardwareStatus)); - var status = await ReceiveAnswerAsync (nameof (GetHardwareStatus)); - return HardwareStatus.Parse (status); + public async Task GetHardwareStatus() + { + await SendCommandAsync(nameof(GetHardwareStatus)); + var status = await ReceiveAnswerAsync(nameof(GetHardwareStatus)); + return HardwareStatus.Parse(status); } /// /// Get whether the robot is in learning mode. /// - public async Task GetLearningMode () { - await SendCommandAsync (nameof (GetLearningMode)); - var mode = await ReceiveAnswerAsync (nameof (GetLearningMode)); - return bool.Parse (mode); + public async Task GetLearningMode() + { + await SendCommandAsync(nameof(GetLearningMode)); + var mode = await ReceiveAnswerAsync(nameof(GetLearningMode)); + return bool.Parse(mode); } /// /// Get the current state of the digital io pins. /// - public async Task GetDigitalIoState () { - await SendCommandAsync (nameof (GetDigitalIoState)); - var state = await ReceiveAnswerAsync (nameof (GetDigitalIoState)); + public async Task GetDigitalIoState() + { + await SendCommandAsync(nameof(GetDigitalIoState)); + var state = await ReceiveAnswerAsync(nameof(GetDigitalIoState)); - var regex = new Regex ("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); - var matches = regex.Matches (state); + var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); + var matches = regex.Matches(state); - return matches.Select (m => DigitalPinObject.Parse (m.Value)).ToArray (); + return matches.Select(m => DigitalPinObject.Parse(m.Value)).ToArray(); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs index 770e8c52..52da1c61 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs @@ -23,9 +23,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; -namespace NiryoOneClient { - public class NiryoOneException : Exception { - public NiryoOneException (string reason) { +namespace NiryoOneClient +{ + public class NiryoOneException : Exception + { + public NiryoOneException(string reason) + { Reason = reason; } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs index b1188c02..7c99f46b 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -26,23 +26,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Collections.Generic; using System.Linq; -namespace NiryoOneClient { - public class PoseObject : IEnumerable { +namespace NiryoOneClient +{ + public class PoseObject : IEnumerable + { private float[] _j = new float[6]; - public PoseObject (float x, float y, float z, float roll, float pitch, float yaw) { - _j = new [] { x, y, z, roll, pitch, yaw }; + public PoseObject(float x, float y, float z, float roll, float pitch, float yaw) + { + _j = new[] { x, y, z, roll, pitch, yaw }; } - public PoseObject (float[] j) { + public PoseObject(float[] j) + { if (j.Length != 6) - throw new ArgumentException ("Joints must be constructed from 6 values.", nameof (j)); + throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); _j = j; } - public static PoseObject Parse (string s) { - return new PoseObject (s.Split (",").Select (float.Parse).ToArray ()); + public static PoseObject Parse(string s) + { + return new PoseObject(s.Split(",").Select(float.Parse).ToArray()); } public float X { get => _j[0]; set => _j[0] = value; } @@ -52,16 +57,19 @@ public static PoseObject Parse (string s) { public float Pitch { get => _j[4]; set => _j[4] = value; } public float Yaw { get => _j[5]; set => _j[5] = value; } - public IEnumerator GetEnumerator () { - return ((IEnumerable) _j).GetEnumerator (); + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_j).GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator () { - return _j.GetEnumerator (); + IEnumerator IEnumerable.GetEnumerator() + { + return _j.GetEnumerator(); } } - public enum RobotAxis { + public enum RobotAxis + { X, Y, Z, diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs index 60af2782..70d6b26f 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -26,23 +26,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Collections.Generic; using System.Linq; -namespace NiryoOneClient { - public class RobotJoints : IEnumerable { +namespace NiryoOneClient +{ + public class RobotJoints : IEnumerable + { private float[] _j = new float[6]; - public RobotJoints (float j1, float j2, float j3, float j4, float j5, float j6) { - _j = new [] { j1, j2, j3, j4, j5, j6 }; + public RobotJoints(float j1, float j2, float j3, float j4, float j5, float j6) + { + _j = new[] { j1, j2, j3, j4, j5, j6 }; } - public RobotJoints (float[] j) { + public RobotJoints(float[] j) + { if (j.Length != 6) - throw new ArgumentException ("Joints must be constructed from 6 values.", nameof (j)); + throw new ArgumentException("Joints must be constructed from 6 values.", nameof(j)); _j = j; } - public static RobotJoints Parse (string s) { - return new RobotJoints (s.Split (",").Select (float.Parse).ToArray ()); + public static RobotJoints Parse(string s) + { + return new RobotJoints(s.Split(",").Select(float.Parse).ToArray()); } public float J1 { get => _j[0]; set => _j[0] = value; } @@ -52,12 +57,14 @@ public static RobotJoints Parse (string s) { public float J5 { get => _j[4]; set => _j[4] = value; } public float J6 { get => _j[5]; set => _j[5] = value; } - public IEnumerator GetEnumerator () { - return ((IEnumerable) _j).GetEnumerator (); + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_j).GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator () { - return _j.GetEnumerator (); + IEnumerator IEnumerable.GetEnumerator() + { + return _j.GetEnumerator(); } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs index 4a30590a..434beef2 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -25,8 +25,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Collections; using System.Collections.Generic; -namespace NiryoOneClient { - public enum RobotPin { +namespace NiryoOneClient +{ + public enum RobotPin + { GPIO_1A, GPIO_1B, GPIO_1C, @@ -35,31 +37,36 @@ public enum RobotPin { GPIO_2C } - public enum PinMode { + public enum PinMode + { OUTPUT = 0, INPUT = 1 } - public enum DigitalState { + public enum DigitalState + { LOW = 0, HIGH = 1 } - public class DigitalPinObject { + public class DigitalPinObject + { public int PinId; public string Name; public PinMode Mode; public DigitalState State; - public static DigitalPinObject Parse (string s) { - if (!s.StartsWith ('[') || !s.EndsWith (']')) - throw new ArgumentException (); + public static DigitalPinObject Parse(string s) + { + if (!s.StartsWith('[') || !s.EndsWith(']')) + throw new ArgumentException(); - var ss = s.Substring (1, s.Length - 2).Split (", "); + var ss = s.Substring(1, s.Length - 2).Split(", "); - return new DigitalPinObject { - PinId = int.Parse (ss[0]), - Name = ss[1].Trim ().Substring (1, ss[1].Length - 2), - Mode = (PinMode) int.Parse (ss[2]), - State = (DigitalState) int.Parse (ss[3]) + return new DigitalPinObject + { + PinId = int.Parse(ss[0]), + Name = ss[1].Trim().Substring(1, ss[1].Length - 2), + Mode = (PinMode)int.Parse(ss[2]), + State = (DigitalState)int.Parse(ss[3]) }; } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs index 8bb16ca6..4accaf9d 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs @@ -25,8 +25,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Collections; using System.Collections.Generic; -namespace NiryoOneClient { - public enum RobotTool { +namespace NiryoOneClient +{ + public enum RobotTool + { GRIPPER_1, GRIPPER_2, GRIPPER_3, From 94322d4e5e05fd2d5cb9f275e9eb59cbd9e0dd6e Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 18:48:38 +0100 Subject: [PATCH 12/38] Remove console logging --- .../clients/csharp/NiryoOneClient/NiryoOneConnection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 9930e4dc..939e3908 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -65,9 +65,7 @@ protected async Task SendCommandAsync(string command_type, params string[] args) cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; else cmd = ToSnakeCaseUpper(command_type); - Console.WriteLine(cmd); await WriteLineAsync(cmd); - Console.WriteLine("r"); } protected async Task ReceiveAnswerAsync(string command_type) From a1ac02d87a764e24c696e8830d45e8ac2b91b611 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 18:50:12 +0100 Subject: [PATCH 13/38] Remove unnecessary usings --- .../clients/csharp/Examples/Program.cs | 25 ++++++++++++++++++- .../csharp/NiryoOneClient/HardwareStatus.cs | 3 --- .../NiryoOneClient/NiryoOneConnection.cs | 2 -- .../csharp/NiryoOneClient/RobotTool.cs | 4 --- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs index 4bed8655..b9d6e6db 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -1,4 +1,27 @@ -using System; +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System; using System.Linq; using System.Threading.Tasks; using NiryoOneClient; diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index b8beb9e2..d3e15e64 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -22,10 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; -using System.Text.Json; namespace NiryoOneClient { diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 939e3908..e4d556f8 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -22,8 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; -using System.Collections; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs index 4accaf9d..974bc743 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs @@ -21,10 +21,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -using System; -using System.Collections; -using System.Collections.Generic; - namespace NiryoOneClient { public enum RobotTool From b2192785cf50ec2f411524514b1217b95fd6dd91 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 18:50:41 +0100 Subject: [PATCH 14/38] More --- niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs index 434beef2..021a4d0a 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -22,8 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; -using System.Collections; -using System.Collections.Generic; namespace NiryoOneClient { From efe655877075152002960c0c6371953ceb7acecb Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 18:58:05 +0100 Subject: [PATCH 15/38] Minifix --- .../clients/csharp/Examples/Program.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs index b9d6e6db..f6b7ee34 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -32,7 +32,13 @@ class Program { public static async Task Main(string[] args) { - string server = args.FirstOrDefault() ?? "10.10.10.10"; + string server = "10.10.10.10"; + + if (args.Length == 1 || args.Length == 7) + { + server = args.FirstOrDefault(); + args = args.Skip(1).ToArray(); + } using (var niryoOneClient = new NiryoOneClient.NiryoOneClient(server)) { @@ -41,6 +47,8 @@ public static async Task Main(string[] args) Console.WriteLine($"Connected!"); PoseObject initialPose = null; + if (args.Length == 6) + initialPose = new PoseObject(args.Select(f => float.Parse(f)).ToArray()); Console.WriteLine("Calibrating..."); await niryo.Calibrate(CalibrateMode.AUTO); From 2613bfede5d99e0ffd9c53cd30fcf4e552d22f80 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 8 Nov 2019 22:03:44 +0100 Subject: [PATCH 16/38] Added documentation --- .../NiryoOneClient/NiryoOneClient.csproj | 4 ++++ .../NiryoOneClient/NiryoOneConnection.cs | 4 ++-- niryo_one_tcp_server/clients/csharp/README.md | 23 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/README.md diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj index ea8231f2..0db46329 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj @@ -13,6 +13,10 @@ A tcp client in C# that manages the tcp communication with a Niryo One robot + + true + + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index e4d556f8..2e7ecf68 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -87,7 +87,7 @@ protected async Task ReceiveAnswerAsync(string command_type) /// /// Request calibration. - /// Whether to request automatic or manual calibration + /// Whether to request automatic or manual calibration /// public async Task Calibrate(CalibrateMode mode) { @@ -157,7 +157,7 @@ public async Task EnableJoystick(bool mode) /// /// Configure a GPIO pin for input or output. - /// + /// public async Task SetPinMode(RobotPin pin, PinMode mode) { await SendCommandAsync(nameof(SetPinMode), pin.ToString(), mode.ToString()); diff --git a/niryo_one_tcp_server/clients/csharp/README.md b/niryo_one_tcp_server/clients/csharp/README.md new file mode 100644 index 00000000..59185db8 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/README.md @@ -0,0 +1,23 @@ +# niryo_one_csharp_tcp_client +Tcp client that communicates with the [tcp server](../..) of the Niryo One +
**Notes:** The TCP server use the underlying [python_api](../../../niryo_one_python_api) package. This package is modeled after the python api client, so the functions available will be similar with this package but may differ in some minor terms, please refer to the current documentation. + +## Connection + +Port of the server: 40001 + +## Building + +* The target framework for the API is .NET Core 3.0. With the SDK for that installed, just run the command `dotnet pack` in the csharp directory, and a NuGet package will be produced in the directory `NiryoOneClient/bin/Release`. + +## Examples + +See the [Examples](Examples) folder for existing scripts. + +## Functions available + +After running `dotnet publish`, xml documentation will be generated at [NiryoOneClient](NiryoOneClient/bin/Debug/netcoreapp3.0/publish/NiryoOneClient.xml). + +## Tests + +Running the command `dotnet test` will compile and run the unit tests available. \ No newline at end of file From 72245faf0562b5b746b22f23f4a86fe2b01927c7 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Sat, 9 Nov 2019 18:36:08 +0100 Subject: [PATCH 17/38] Remove ToSnakeCaseUpper and dependency on the method names --- .../NiryoOneClient/NiryoOneConnection.cs | 104 +++++++++--------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 2e7ecf68..e0ff0b89 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -1,3 +1,4 @@ + /* MIT License Copyright (c) 2019 Niryo @@ -51,18 +52,13 @@ internal async Task ReadLineAsync() return await _textReader.ReadLineAsync(); } - protected string ToSnakeCaseUpper(string s) - { - return string.Concat(s.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); - } - protected async Task SendCommandAsync(string command_type, params string[] args) { string cmd; if (args.Any()) - cmd = $"{ToSnakeCaseUpper(command_type)}:{string.Join(",", args)}"; + cmd = $"{command_type}:{string.Join(",", args)}"; else - cmd = ToSnakeCaseUpper(command_type); + cmd = command_type; await WriteLineAsync(cmd); } @@ -72,7 +68,7 @@ protected async Task ReceiveAnswerAsync(string command_type) result = result.TrimEnd('\n'); var colonSplit = result.Split(':', 2); var cmd = colonSplit[0]; - if (cmd != ToSnakeCaseUpper(command_type)) + if (cmd != command_type) throw new NiryoOneException("Wrong command response received."); var commaSplit2 = colonSplit[1].Split(',', 2); var status = commaSplit2[0]; @@ -91,8 +87,8 @@ protected async Task ReceiveAnswerAsync(string command_type) ///
public async Task Calibrate(CalibrateMode mode) { - await SendCommandAsync(nameof(Calibrate), mode.ToString()); - await ReceiveAnswerAsync(nameof(Calibrate)); + await SendCommandAsync("CALIBRATE", mode.ToString()); + await ReceiveAnswerAsync("CALIBRATE"); } /// @@ -101,8 +97,8 @@ public async Task Calibrate(CalibrateMode mode) /// public async Task SetLearningMode(bool mode) { - await SendCommandAsync(nameof(SetLearningMode), mode.ToString().ToUpper()); - await ReceiveAnswerAsync(nameof(SetLearningMode)); + await SendCommandAsync("SET_LEARNING_MODE", mode.ToString().ToUpper()); + await ReceiveAnswerAsync("SET_LEARNING_MODE"); } /// @@ -111,8 +107,8 @@ public async Task SetLearningMode(bool mode) /// public async Task MoveJoints(RobotJoints joints) { - await SendCommandAsync(nameof(MoveJoints), string.Join(',', joints)); - await ReceiveAnswerAsync(nameof(MoveJoints)); + await SendCommandAsync("MOVE_JOINTS", string.Join(',', joints)); + await ReceiveAnswerAsync("MOVE_JOINTS"); } /// @@ -121,8 +117,8 @@ public async Task MoveJoints(RobotJoints joints) /// public async Task MovePose(PoseObject pose) { - await SendCommandAsync(nameof(MovePose), string.Join(',', pose)); - await ReceiveAnswerAsync(nameof(MovePose)); + await SendCommandAsync("MOVE_POSE", string.Join(',', pose)); + await ReceiveAnswerAsync("MOVE_POSE"); } /// @@ -132,8 +128,8 @@ public async Task MovePose(PoseObject pose) /// public async Task ShiftPose(RobotAxis axis, float value) { - await SendCommandAsync(nameof(ShiftPose), axis.ToString(), value.ToString()); - await ReceiveAnswerAsync(nameof(ShiftPose)); + await SendCommandAsync("SHIFT_POSE", axis.ToString(), value.ToString()); + await ReceiveAnswerAsync("SHIFT_POSE"); } /// @@ -142,8 +138,8 @@ public async Task ShiftPose(RobotAxis axis, float value) /// public async Task SetArmMaxVelocity(int velocity) { - await SendCommandAsync(nameof(SetArmMaxVelocity), velocity.ToString()); - await ReceiveAnswerAsync(nameof(SetArmMaxVelocity)); + await SendCommandAsync("SET_ARM_MAX_VELOCITY", velocity.ToString()); + await ReceiveAnswerAsync("SET_ARM_MAX_VELOCITY"); } /// @@ -151,8 +147,8 @@ public async Task SetArmMaxVelocity(int velocity) /// public async Task EnableJoystick(bool mode) { - await SendCommandAsync(nameof(EnableJoystick), mode.ToString().ToUpper()); - await ReceiveAnswerAsync(nameof(EnableJoystick)); + await SendCommandAsync("ENABLE_JOYSTICK", mode.ToString().ToUpper()); + await ReceiveAnswerAsync("ENABLE_JOYSTICK"); } /// @@ -160,8 +156,8 @@ public async Task EnableJoystick(bool mode) /// public async Task SetPinMode(RobotPin pin, PinMode mode) { - await SendCommandAsync(nameof(SetPinMode), pin.ToString(), mode.ToString()); - await ReceiveAnswerAsync(nameof(SetPinMode)); + await SendCommandAsync("SET_PIN_MODE", pin.ToString(), mode.ToString()); + await ReceiveAnswerAsync("SET_PIN_MODE"); } /// @@ -169,8 +165,8 @@ public async Task SetPinMode(RobotPin pin, PinMode mode) /// public async Task DigitalWrite(RobotPin pin, DigitalState state) { - await SendCommandAsync(nameof(DigitalWrite), pin.ToString(), state.ToString()); - await ReceiveAnswerAsync(nameof(DigitalWrite)); + await SendCommandAsync("DIGITAL_WRITE", pin.ToString(), state.ToString()); + await ReceiveAnswerAsync("DIGITAL_WRITE"); } /// @@ -178,8 +174,8 @@ public async Task DigitalWrite(RobotPin pin, DigitalState state) /// public async Task DigitalRead(RobotPin pin) { - await SendCommandAsync(nameof(DigitalRead), pin.ToString()); - var state = await ReceiveAnswerAsync(nameof(DigitalRead)); + await SendCommandAsync("DIGITAL_READ", pin.ToString()); + var state = await ReceiveAnswerAsync("DIGITAL_READ"); return (DigitalState)Enum.Parse(typeof(DigitalState), state); } @@ -188,8 +184,8 @@ public async Task DigitalRead(RobotPin pin) ///
public async Task ChangeTool(RobotTool tool) { - await SendCommandAsync(nameof(ChangeTool), tool.ToString()); - await ReceiveAnswerAsync(nameof(ChangeTool)); + await SendCommandAsync("CHANGE_TOOL", tool.ToString()); + await ReceiveAnswerAsync("CHANGE_TOOL"); } /// @@ -199,8 +195,8 @@ public async Task ChangeTool(RobotTool tool) /// public async Task OpenGripper(RobotTool gripper, int speed) { - await SendCommandAsync(nameof(OpenGripper), gripper.ToString(), speed.ToString()); - await ReceiveAnswerAsync(nameof(OpenGripper)); + await SendCommandAsync("OPEN_GRIPPER", gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync("OPEN_GRIPPER"); } /// @@ -210,8 +206,8 @@ public async Task OpenGripper(RobotTool gripper, int speed) /// public async Task CloseGripper(RobotTool gripper, int speed) { - await SendCommandAsync(nameof(CloseGripper), gripper.ToString(), speed.ToString()); - await ReceiveAnswerAsync(nameof(CloseGripper)); + await SendCommandAsync("CLOSE_GRIPPER", gripper.ToString(), speed.ToString()); + await ReceiveAnswerAsync("CLOSE_GRIPPER"); } /// @@ -220,8 +216,8 @@ public async Task CloseGripper(RobotTool gripper, int speed) /// public async Task PullAirVacuumPump(RobotTool vacuumPump) { - await SendCommandAsync(nameof(PullAirVacuumPump), vacuumPump.ToString()); - await ReceiveAnswerAsync(nameof(PullAirVacuumPump)); + await SendCommandAsync("PULL_AIR_VACUUM_PUMP", vacuumPump.ToString()); + await ReceiveAnswerAsync("PULL_AIR_VACUUM_PUMP"); } /// @@ -230,8 +226,8 @@ public async Task PullAirVacuumPump(RobotTool vacuumPump) /// public async Task PushAirVacuumPump(RobotTool vacuumPump) { - await SendCommandAsync(nameof(PushAirVacuumPump), vacuumPump.ToString()); - await ReceiveAnswerAsync(nameof(PushAirVacuumPump)); + await SendCommandAsync("PUSH_AIR_VACUUM_PUMP", vacuumPump.ToString()); + await ReceiveAnswerAsync("PUSH_AIR_VACUUM_PUMP"); } /// @@ -241,8 +237,8 @@ public async Task PushAirVacuumPump(RobotTool vacuumPump) /// public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) { - await SendCommandAsync(nameof(SetupElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(SetupElectromagnet)); + await SendCommandAsync("SETUP_ELECTROMAGNET", tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync("SETUP_ELECTROMAGNET"); } /// @@ -252,8 +248,8 @@ public async Task SetupElectromagnet(RobotTool tool, RobotPin pin) /// public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) { - await SendCommandAsync(nameof(ActivateElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(ActivateElectromagnet)); + await SendCommandAsync("ACTIVATE_ELECTROMAGNET", tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync("ACTIVATE_ELECTROMAGNET"); } /// @@ -263,8 +259,8 @@ public async Task ActivateElectromagnet(RobotTool tool, RobotPin pin) /// public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) { - await SendCommandAsync(nameof(DeactivateElectromagnet), tool.ToString(), pin.ToString()); - await ReceiveAnswerAsync(nameof(DeactivateElectromagnet)); + await SendCommandAsync("DEACTIVATE_ELECTROMAGNET", tool.ToString(), pin.ToString()); + await ReceiveAnswerAsync("DEACTIVATE_ELECTROMAGNET"); } /// @@ -272,8 +268,8 @@ public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) /// public async Task GetJoints() { - await SendCommandAsync(nameof(GetJoints)); - var joints = await ReceiveAnswerAsync(nameof(GetJoints)); + await SendCommandAsync("GET_JOINTS"); + var joints = await ReceiveAnswerAsync("GET_JOINTS"); return RobotJoints.Parse(joints); } @@ -282,8 +278,8 @@ public async Task GetJoints() ///
public async Task GetPose() { - await SendCommandAsync(nameof(GetPose)); - var pose = await ReceiveAnswerAsync(nameof(GetPose)); + await SendCommandAsync("GET_POSE"); + var pose = await ReceiveAnswerAsync("GET_POSE"); return PoseObject.Parse(pose); } @@ -292,8 +288,8 @@ public async Task GetPose() ///
public async Task GetHardwareStatus() { - await SendCommandAsync(nameof(GetHardwareStatus)); - var status = await ReceiveAnswerAsync(nameof(GetHardwareStatus)); + await SendCommandAsync("GET_HARDWARE_STATUS"); + var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS"); return HardwareStatus.Parse(status); } @@ -302,8 +298,8 @@ public async Task GetHardwareStatus() ///
public async Task GetLearningMode() { - await SendCommandAsync(nameof(GetLearningMode)); - var mode = await ReceiveAnswerAsync(nameof(GetLearningMode)); + await SendCommandAsync("GET_LEARNING_MODE"); + var mode = await ReceiveAnswerAsync("GET_LEARNING_MODE"); return bool.Parse(mode); } @@ -312,8 +308,8 @@ public async Task GetLearningMode() /// public async Task GetDigitalIoState() { - await SendCommandAsync(nameof(GetDigitalIoState)); - var state = await ReceiveAnswerAsync(nameof(GetDigitalIoState)); + await SendCommandAsync("GET_DIGITAL_IO_STATE"); + var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE"); var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); var matches = regex.Matches(state); From 23eb49399f9af0e56f87fc94695d9c15196c20ec Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Sat, 9 Nov 2019 18:38:10 +0100 Subject: [PATCH 18/38] Rename GetDigitalIoState to GetDigitalIOState, per regular C# rules --- niryo_one_tcp_server/clients/csharp/Examples/Program.cs | 4 ++-- .../csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs | 4 ++-- .../clients/csharp/NiryoOneClient/NiryoOneConnection.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs index f6b7ee34..2ff6e358 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -63,9 +63,9 @@ public static async Task Main(string[] args) await niryo.MovePose(initialPose); } - var digitalIoPins = await niryo.GetDigitalIoState(); + var digitalIOPins = await niryo.GetDigitalIOState(); - foreach (var pin in digitalIoPins) + foreach (var pin in digitalIOPins) Console.WriteLine($"Pin: {pin.PinId}, name: {pin.Name}, mode: {pin.Mode}, state: {pin.State}"); await niryo.SetLearningMode(true); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 8ecef0fa..afe2ef31 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -306,12 +306,12 @@ public async Task GetLearningMode_Sample_Works() } [TestMethod] - public async Task GetDigitalIoState_Sample_Works() + public async Task GetDigitalIOState_Sample_Works() { _streamReader.ReadLineAsync().Returns(Task.FromResult( "GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]" )); - var state = await _connection.GetDigitalIoState(); + var state = await _connection.GetDigitalIOState(); Assert.AreEqual(2, state[0].PinId); Assert.AreEqual("1A", state[0].Name); Assert.AreEqual(PinMode.INPUT, state[0].Mode); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index e0ff0b89..741cb761 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -306,7 +306,7 @@ public async Task GetLearningMode() /// /// Get the current state of the digital io pins. /// - public async Task GetDigitalIoState() + public async Task GetDigitalIOState() { await SendCommandAsync("GET_DIGITAL_IO_STATE"); var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE"); From 5e38d9278cf66f48edf48baea11e44df384d13d1 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Sat, 9 Nov 2019 18:44:45 +0100 Subject: [PATCH 19/38] Add coverage report to test run --- .../clients/csharp/.vscode/tasks.json | 8 +- .../NiryoOneClient.Tests.csproj | 9 +- .../csharp/NiryoOneClient.Tests/coverage.json | 1544 +++++++++++++++++ 3 files changed, 1555 insertions(+), 6 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json diff --git a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json index 1f945400..974c4441 100644 --- a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json +++ b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json @@ -28,10 +28,7 @@ "--include-symbols" ], "problemMatcher": "$msCompile", - "group": { - "kind": "build", - "isDefault": false - } + "group": "build" }, { "label": "publish", @@ -57,7 +54,8 @@ "test", "${workspaceFolder}/NiryoOneClient.sln", "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" + "/consoleloggerparameters:NoSummary", + "/p:CollectCoverage=true" ], "problemMatcher": "$msCompile" }, diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj index 43ee5477..c62b56ee 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj @@ -7,10 +7,17 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json new file mode 100644 index 00000000..8a08c96c --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json @@ -0,0 +1,1544 @@ +{ + "NiryoOneClient.dll": { + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs": { + "NiryoOneClient.HardwareStatus": { + "System.String NiryoOneClient.HardwareStatus::Strip_(System.String,System.Char,System.Char)": { + "Lines": { + "32": 26, + "33": 26, + "34": 0, + "35": 26, + "36": 0, + "37": 26, + "38": 26 + }, + "Branches": [ + { + "Line": 33, + "Offset": 13, + "EndOffset": 15, + "Path": 0, + "Ordinal": 0, + "Hits": 0 + }, + { + "Line": 33, + "Offset": 13, + "EndOffset": 21, + "Path": 1, + "Ordinal": 1, + "Hits": 26 + }, + { + "Line": 35, + "Offset": 33, + "EndOffset": 35, + "Path": 0, + "Ordinal": 2, + "Hits": 0 + }, + { + "Line": 35, + "Offset": 33, + "EndOffset": 41, + "Path": 1, + "Ordinal": 3, + "Hits": 26 + } + ] + }, + "System.String[] NiryoOneClient.HardwareStatus::ParseStrings_(System.String)": { + "Lines": { + "41": 4, + "42": 4, + "43": 28, + "44": 4 + }, + "Branches": [ + { + "Line": 43, + "Offset": 25, + "EndOffset": 27, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 43, + "Offset": 25, + "EndOffset": 50, + "Path": 1, + "Ordinal": 1, + "Hits": 4 + } + ] + }, + "T[] NiryoOneClient.HardwareStatus::ParseNumbers_(System.String,System.Func`2)": { + "Lines": { + "47": 6, + "48": 6, + "49": 42, + "50": 6 + }, + "Branches": [] + }, + "NiryoOneClient.HardwareStatus NiryoOneClient.HardwareStatus::Parse(System.String)": { + "Lines": { + "53": 2, + "54": 2, + "55": 2, + "57": 2, + "58": 0, + "59": 0, + "62": 2, + "63": 2, + "64": 2, + "65": 2, + "66": 2, + "67": 2, + "69": 2, + "70": 2, + "72": 2, + "73": 2, + "74": 2, + "76": 2, + "77": 2, + "78": 2, + "79": 2, + "80": 2, + "81": 2, + "82": 2, + "83": 2, + "84": 2, + "85": 2, + "86": 2, + "87": 2, + "88": 2, + "89": 2, + "90": 2, + "91": 2 + }, + "Branches": [ + { + "Line": 57, + "Offset": 37, + "EndOffset": 39, + "Path": 0, + "Ordinal": 0, + "Hits": 0 + }, + { + "Line": 57, + "Offset": 37, + "EndOffset": 51, + "Path": 1, + "Ordinal": 1, + "Hits": 2 + } + ] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs": { + "NiryoOneClient.NiryoOneClient": { + "System.Void NiryoOneClient.NiryoOneClient::Dispose()": { + "Lines": { + "66": 0, + "67": 0, + "68": 0, + "69": 0, + "70": 0, + "71": 0, + "72": 0, + "73": 0 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.NiryoOneClient::.ctor(System.String,System.Int32)": { + "Lines": { + "44": 0, + "45": 0, + "46": 0, + "47": 0, + "48": 0 + }, + "Branches": [] + } + }, + "NiryoOneClient.NiryoOneClient/d__6": { + "System.Void NiryoOneClient.NiryoOneClient/d__6::MoveNext()": { + "Lines": { + "51": 0, + "52": 0, + "53": 0, + "54": 0, + "55": 0, + "56": 0, + "58": 0, + "59": 0, + "60": 0, + "61": 0, + "62": 0, + "63": 0 + }, + "Branches": [ + { + "Line": 52, + "Offset": 34, + "EndOffset": 36, + "Path": 0, + "Ordinal": 0, + "Hits": 0 + }, + { + "Line": 52, + "Offset": 34, + "EndOffset": 67, + "Path": 1, + "Ordinal": 1, + "Hits": 0 + }, + { + "Line": 59, + "Offset": 134, + "EndOffset": 136, + "Path": 0, + "Ordinal": 2, + "Hits": 0 + }, + { + "Line": 59, + "Offset": 134, + "EndOffset": 204, + "Path": 1, + "Ordinal": 3, + "Hits": 0 + } + ] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs": { + "NiryoOneClient.NiryoOneConnection": { + "System.Void NiryoOneClient.NiryoOneConnection::.ctor(System.IO.TextReader,System.IO.TextWriter)": { + "Lines": { + "38": 28, + "39": 28, + "40": 28, + "41": 28, + "42": 28 + }, + "Branches": [] + } + }, + "NiryoOneClient.NiryoOneConnection/d__3": { + "System.Void NiryoOneClient.NiryoOneConnection/d__3::MoveNext()": { + "Lines": { + "45": 28, + "46": 28, + "47": 28, + "48": 28 + }, + "Branches": [ + { + "Line": 45, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 28 + }, + { + "Line": 46, + "Offset": 61, + "EndOffset": 130, + "Path": 1, + "Ordinal": 3, + "Hits": 28 + }, + { + "Line": 47, + "Offset": 167, + "EndOffset": 233, + "Path": 1, + "Ordinal": 5, + "Hits": 28 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__4": { + "System.Void NiryoOneClient.NiryoOneConnection/d__4::MoveNext()": { + "Lines": { + "51": 28, + "52": 28, + "53": 28 + }, + "Branches": [ + { + "Line": 52, + "Offset": 44, + "EndOffset": 110, + "Path": 1, + "Ordinal": 1, + "Hits": 28 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__5": { + "System.Void NiryoOneClient.NiryoOneConnection/d__5::MoveNext()": { + "Lines": { + "56": 28, + "58": 28, + "59": 23, + "61": 5, + "62": 28, + "63": 28 + }, + "Branches": [ + { + "Line": 58, + "Offset": 31, + "EndOffset": 33, + "Path": 0, + "Ordinal": 0, + "Hits": 23 + }, + { + "Line": 58, + "Offset": 31, + "EndOffset": 73, + "Path": 1, + "Ordinal": 1, + "Hits": 5 + }, + { + "Line": 62, + "Offset": 115, + "EndOffset": 181, + "Path": 1, + "Ordinal": 3, + "Hits": 28 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__6": { + "System.Void NiryoOneClient.NiryoOneConnection/d__6::MoveNext()": { + "Lines": { + "66": 28, + "67": 28, + "68": 28, + "69": 28, + "70": 28, + "71": 28, + "72": 1, + "73": 27, + "74": 27, + "75": 27, + "76": 2, + "78": 25, + "79": 6, + "81": 19, + "82": 25 + }, + "Branches": [ + { + "Line": 67, + "Offset": 39, + "EndOffset": 108, + "Path": 1, + "Ordinal": 1, + "Hits": 28 + }, + { + "Line": 71, + "Offset": 215, + "EndOffset": 217, + "Path": 0, + "Ordinal": 2, + "Hits": 1 + }, + { + "Line": 71, + "Offset": 215, + "EndOffset": 228, + "Path": 1, + "Ordinal": 3, + "Hits": 27 + }, + { + "Line": 75, + "Offset": 285, + "EndOffset": 287, + "Path": 0, + "Ordinal": 4, + "Hits": 2 + }, + { + "Line": 75, + "Offset": 285, + "EndOffset": 315, + "Path": 1, + "Ordinal": 5, + "Hits": 25 + }, + { + "Line": 78, + "Offset": 330, + "EndOffset": 332, + "Path": 0, + "Ordinal": 6, + "Hits": 6 + }, + { + "Line": 78, + "Offset": 330, + "EndOffset": 343, + "Path": 1, + "Ordinal": 7, + "Hits": 19 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__7": { + "System.Void NiryoOneClient.NiryoOneConnection/d__7::MoveNext()": { + "Lines": { + "89": 3, + "90": 3, + "91": 3, + "92": 2 + }, + "Branches": [ + { + "Line": 89, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 3 + }, + { + "Line": 90, + "Offset": 81, + "EndOffset": 150, + "Path": 1, + "Ordinal": 3, + "Hits": 3 + }, + { + "Line": 91, + "Offset": 187, + "EndOffset": 253, + "Path": 1, + "Ordinal": 5, + "Hits": 3 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__8": { + "System.Void NiryoOneClient.NiryoOneConnection/d__8::MoveNext()": { + "Lines": { + "99": 4, + "100": 4, + "101": 4, + "102": 2 + }, + "Branches": [ + { + "Line": 99, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 4 + }, + { + "Line": 100, + "Offset": 80, + "EndOffset": 149, + "Path": 1, + "Ordinal": 3, + "Hits": 4 + }, + { + "Line": 101, + "Offset": 186, + "EndOffset": 252, + "Path": 1, + "Ordinal": 5, + "Hits": 4 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__9": { + "System.Void NiryoOneClient.NiryoOneConnection/d__9::MoveNext()": { + "Lines": { + "109": 1, + "110": 1, + "111": 1, + "112": 1 + }, + "Branches": [ + { + "Line": 109, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 110, + "Offset": 77, + "EndOffset": 146, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 111, + "Offset": 183, + "EndOffset": 249, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__10": { + "System.Void NiryoOneClient.NiryoOneConnection/d__10::MoveNext()": { + "Lines": { + "119": 1, + "120": 1, + "121": 1, + "122": 1 + }, + "Branches": [ + { + "Line": 119, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 120, + "Offset": 77, + "EndOffset": 146, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 121, + "Offset": 183, + "EndOffset": 249, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__11": { + "System.Void NiryoOneClient.NiryoOneConnection/d__11::MoveNext()": { + "Lines": { + "130": 1, + "131": 1, + "132": 1, + "133": 1 + }, + "Branches": [ + { + "Line": 130, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 131, + "Offset": 95, + "EndOffset": 164, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 132, + "Offset": 201, + "EndOffset": 267, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__12": { + "System.Void NiryoOneClient.NiryoOneConnection/d__12::MoveNext()": { + "Lines": { + "140": 1, + "141": 1, + "142": 1, + "143": 1 + }, + "Branches": [ + { + "Line": 140, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 141, + "Offset": 75, + "EndOffset": 144, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 142, + "Offset": 181, + "EndOffset": 247, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__13": { + "System.Void NiryoOneClient.NiryoOneConnection/d__13::MoveNext()": { + "Lines": { + "149": 1, + "150": 1, + "151": 1, + "152": 1 + }, + "Branches": [ + { + "Line": 149, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 150, + "Offset": 80, + "EndOffset": 149, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 151, + "Offset": 186, + "EndOffset": 252, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__14": { + "System.Void NiryoOneClient.NiryoOneConnection/d__14::MoveNext()": { + "Lines": { + "158": 1, + "159": 1, + "160": 1, + "161": 1 + }, + "Branches": [ + { + "Line": 158, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 159, + "Offset": 101, + "EndOffset": 170, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 160, + "Offset": 207, + "EndOffset": 273, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__15": { + "System.Void NiryoOneClient.NiryoOneConnection/d__15::MoveNext()": { + "Lines": { + "167": 1, + "168": 1, + "169": 1, + "170": 1 + }, + "Branches": [ + { + "Line": 167, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 168, + "Offset": 101, + "EndOffset": 170, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 169, + "Offset": 207, + "EndOffset": 273, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__16": { + "System.Void NiryoOneClient.NiryoOneConnection/d__16::MoveNext()": { + "Lines": { + "176": 1, + "177": 1, + "178": 1, + "179": 1, + "180": 1 + }, + "Branches": [ + { + "Line": 176, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 177, + "Offset": 81, + "EndOffset": 150, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 178, + "Offset": 188, + "EndOffset": 259, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__17": { + "System.Void NiryoOneClient.NiryoOneConnection/d__17::MoveNext()": { + "Lines": { + "186": 1, + "187": 1, + "188": 1, + "189": 1 + }, + "Branches": [ + { + "Line": 186, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 187, + "Offset": 81, + "EndOffset": 150, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 188, + "Offset": 187, + "EndOffset": 253, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__18": { + "System.Void NiryoOneClient.NiryoOneConnection/d__18::MoveNext()": { + "Lines": { + "197": 1, + "198": 1, + "199": 1, + "200": 1 + }, + "Branches": [ + { + "Line": 197, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 198, + "Offset": 95, + "EndOffset": 164, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 199, + "Offset": 201, + "EndOffset": 267, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__19": { + "System.Void NiryoOneClient.NiryoOneConnection/d__19::MoveNext()": { + "Lines": { + "208": 1, + "209": 1, + "210": 1, + "211": 1 + }, + "Branches": [ + { + "Line": 208, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 209, + "Offset": 95, + "EndOffset": 164, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 210, + "Offset": 201, + "EndOffset": 267, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__20": { + "System.Void NiryoOneClient.NiryoOneConnection/d__20::MoveNext()": { + "Lines": { + "218": 1, + "219": 1, + "220": 1, + "221": 1 + }, + "Branches": [ + { + "Line": 218, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 219, + "Offset": 81, + "EndOffset": 150, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 220, + "Offset": 187, + "EndOffset": 253, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__21": { + "System.Void NiryoOneClient.NiryoOneConnection/d__21::MoveNext()": { + "Lines": { + "228": 1, + "229": 1, + "230": 1, + "231": 1 + }, + "Branches": [ + { + "Line": 228, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 229, + "Offset": 81, + "EndOffset": 150, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 230, + "Offset": 187, + "EndOffset": 253, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__22": { + "System.Void NiryoOneClient.NiryoOneConnection/d__22::MoveNext()": { + "Lines": { + "239": 1, + "240": 1, + "241": 1, + "242": 1 + }, + "Branches": [ + { + "Line": 239, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 240, + "Offset": 101, + "EndOffset": 170, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 241, + "Offset": 207, + "EndOffset": 273, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__23": { + "System.Void NiryoOneClient.NiryoOneConnection/d__23::MoveNext()": { + "Lines": { + "250": 1, + "251": 1, + "252": 1, + "253": 1 + }, + "Branches": [ + { + "Line": 250, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 251, + "Offset": 101, + "EndOffset": 170, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 252, + "Offset": 207, + "EndOffset": 273, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__24": { + "System.Void NiryoOneClient.NiryoOneConnection/d__24::MoveNext()": { + "Lines": { + "261": 1, + "262": 1, + "263": 1, + "264": 1 + }, + "Branches": [ + { + "Line": 261, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 262, + "Offset": 101, + "EndOffset": 170, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 263, + "Offset": 207, + "EndOffset": 273, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__25": { + "System.Void NiryoOneClient.NiryoOneConnection/d__25::MoveNext()": { + "Lines": { + "270": 1, + "271": 1, + "272": 1, + "273": 1, + "274": 1 + }, + "Branches": [ + { + "Line": 270, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 271, + "Offset": 60, + "EndOffset": 129, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 272, + "Offset": 167, + "EndOffset": 235, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__26": { + "System.Void NiryoOneClient.NiryoOneConnection/d__26::MoveNext()": { + "Lines": { + "280": 1, + "281": 1, + "282": 1, + "283": 1, + "284": 1 + }, + "Branches": [ + { + "Line": 280, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 281, + "Offset": 60, + "EndOffset": 129, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 282, + "Offset": 167, + "EndOffset": 235, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__27": { + "System.Void NiryoOneClient.NiryoOneConnection/d__27::MoveNext()": { + "Lines": { + "290": 1, + "291": 1, + "292": 1, + "293": 1, + "294": 1 + }, + "Branches": [ + { + "Line": 290, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 291, + "Offset": 60, + "EndOffset": 129, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 292, + "Offset": 167, + "EndOffset": 235, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/d__28": { + "System.Void NiryoOneClient.NiryoOneConnection/d__28::MoveNext()": { + "Lines": { + "300": 1, + "301": 1, + "302": 1, + "303": 1, + "304": 1 + }, + "Branches": [ + { + "Line": 300, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 301, + "Offset": 60, + "EndOffset": 129, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 302, + "Offset": 167, + "EndOffset": 235, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + } + ] + } + }, + "NiryoOneClient.NiryoOneConnection/<>c": { + "NiryoOneClient.DigitalPinObject NiryoOneClient.NiryoOneConnection/<>c::b__29_0(System.Text.RegularExpressions.Match)": { + "Lines": { + "317": 9 + }, + "Branches": [] + } + }, + "NiryoOneClient.NiryoOneConnection/d__29": { + "System.Void NiryoOneClient.NiryoOneConnection/d__29::MoveNext()": { + "Lines": { + "310": 1, + "311": 1, + "312": 1, + "314": 1, + "315": 1, + "318": 1 + }, + "Branches": [ + { + "Line": 310, + "Offset": 14, + "EndOffset": 25, + "Path": 0, + "Ordinal": 0, + "Hits": 1 + }, + { + "Line": 311, + "Offset": 60, + "EndOffset": 129, + "Path": 1, + "Ordinal": 3, + "Hits": 1 + }, + { + "Line": 312, + "Offset": 167, + "EndOffset": 238, + "Path": 1, + "Ordinal": 5, + "Hits": 1 + }, + { + "Line": 317, + "Offset": 321, + "EndOffset": 323, + "Path": 0, + "Ordinal": 6, + "Hits": 1 + }, + { + "Line": 317, + "Offset": 321, + "EndOffset": 346, + "Path": 1, + "Ordinal": 7, + "Hits": 1 + } + ] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs": { + "NiryoOneClient.NiryoOneException": { + "System.String NiryoOneClient.NiryoOneException::get_Reason()": { + "Lines": { + "35": 3 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.NiryoOneException::.ctor(System.String)": { + "Lines": { + "30": 3, + "31": 3, + "32": 3, + "33": 3 + }, + "Branches": [] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs": { + "NiryoOneClient.PoseObject": { + "NiryoOneClient.PoseObject NiryoOneClient.PoseObject::Parse(System.String)": { + "Lines": { + "49": 1, + "50": 1, + "51": 1 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_X()": { + "Lines": { + "53": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_Y()": { + "Lines": { + "54": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_Z()": { + "Lines": { + "55": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_Roll()": { + "Lines": { + "56": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_Pitch()": { + "Lines": { + "57": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.PoseObject::get_Yaw()": { + "Lines": { + "58": 0 + }, + "Branches": [] + }, + "System.Collections.Generic.IEnumerator`1 NiryoOneClient.PoseObject::GetEnumerator()": { + "Lines": { + "61": 2, + "62": 2, + "63": 2 + }, + "Branches": [] + }, + "System.Collections.IEnumerator NiryoOneClient.PoseObject::System.Collections.IEnumerable.GetEnumerator()": { + "Lines": { + "66": 0, + "67": 0, + "68": 0 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.PoseObject::.ctor(System.Single,System.Single,System.Single,System.Single,System.Single,System.Single)": { + "Lines": { + "33": 2, + "35": 0, + "36": 0, + "37": 0, + "38": 0 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.PoseObject::.ctor(System.Single[])": { + "Lines": { + "40": 2, + "41": 2, + "42": 2, + "43": 0, + "45": 2, + "46": 2 + }, + "Branches": [ + { + "Line": 42, + "Offset": 31, + "EndOffset": 33, + "Path": 0, + "Ordinal": 0, + "Hits": 0 + }, + { + "Line": 42, + "Offset": 31, + "EndOffset": 49, + "Path": 1, + "Ordinal": 1, + "Hits": 2 + } + ] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs": { + "NiryoOneClient.RobotJoints": { + "NiryoOneClient.RobotJoints NiryoOneClient.RobotJoints::Parse(System.String)": { + "Lines": { + "49": 1, + "50": 1, + "51": 1 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J1()": { + "Lines": { + "53": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J2()": { + "Lines": { + "54": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J3()": { + "Lines": { + "55": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J4()": { + "Lines": { + "56": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J5()": { + "Lines": { + "57": 0 + }, + "Branches": [] + }, + "System.Single NiryoOneClient.RobotJoints::get_J6()": { + "Lines": { + "58": 0 + }, + "Branches": [] + }, + "System.Collections.Generic.IEnumerator`1 NiryoOneClient.RobotJoints::GetEnumerator()": { + "Lines": { + "61": 2, + "62": 2, + "63": 2 + }, + "Branches": [] + }, + "System.Collections.IEnumerator NiryoOneClient.RobotJoints::System.Collections.IEnumerable.GetEnumerator()": { + "Lines": { + "66": 0, + "67": 0, + "68": 0 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.RobotJoints::.ctor(System.Single,System.Single,System.Single,System.Single,System.Single,System.Single)": { + "Lines": { + "33": 2, + "35": 0, + "36": 0, + "37": 0, + "38": 0 + }, + "Branches": [] + }, + "System.Void NiryoOneClient.RobotJoints::.ctor(System.Single[])": { + "Lines": { + "40": 2, + "41": 2, + "42": 2, + "43": 0, + "45": 2, + "46": 2 + }, + "Branches": [ + { + "Line": 42, + "Offset": 31, + "EndOffset": 33, + "Path": 0, + "Ordinal": 0, + "Hits": 0 + }, + { + "Line": 42, + "Offset": 31, + "EndOffset": 49, + "Path": 1, + "Ordinal": 1, + "Hits": 2 + } + ] + } + } + }, + "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs": { + "NiryoOneClient.DigitalPinObject": { + "NiryoOneClient.DigitalPinObject NiryoOneClient.DigitalPinObject::Parse(System.String)": { + "Lines": { + "56": 8, + "57": 8, + "58": 0, + "60": 8, + "62": 8, + "63": 8, + "64": 8, + "65": 8, + "66": 8, + "67": 8, + "68": 8, + "69": 8 + }, + "Branches": [ + { + "Line": 57, + "Offset": 9, + "EndOffset": 11, + "Path": 0, + "Ordinal": 0, + "Hits": 8 + }, + { + "Line": 57, + "Offset": 9, + "EndOffset": 24, + "Path": 1, + "Ordinal": 1, + "Hits": 0 + }, + { + "Line": 57, + "Offset": 27, + "EndOffset": 29, + "Path": 0, + "Ordinal": 2, + "Hits": 0 + }, + { + "Line": 57, + "Offset": 27, + "EndOffset": 35, + "Path": 1, + "Ordinal": 3, + "Hits": 8 + } + ] + } + } + } + } +} \ No newline at end of file From ea793692276b7ccf7ab7a91c13fd099ba55c29d1 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Sun, 10 Nov 2019 23:28:02 +0100 Subject: [PATCH 20/38] Use invariant culture for floating point string ops --- niryo_one_tcp_server/clients/csharp/Examples/Program.cs | 2 +- .../clients/csharp/NiryoOneClient/HardwareStatus.cs | 3 ++- .../clients/csharp/NiryoOneClient/NiryoOneConnection.cs | 7 ++++--- .../clients/csharp/NiryoOneClient/PoseObject.cs | 3 ++- .../clients/csharp/NiryoOneClient/RobotJoints.cs | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs index 2ff6e358..69f979f6 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -48,7 +48,7 @@ public static async Task Main(string[] args) PoseObject initialPose = null; if (args.Length == 6) - initialPose = new PoseObject(args.Select(f => float.Parse(f)).ToArray()); + initialPose = new PoseObject(args.Select(f => float.Parse(f, CultureInfo.InvariantCulture).ToArray()); Console.WriteLine("Calibrating..."); await niryo.Calibrate(CalibrateMode.AUTO); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index d3e15e64..c152cb78 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; +using System.Globalization; using System.Linq; namespace NiryoOneClient @@ -70,7 +71,7 @@ public static HardwareStatus Parse(string data) var motorTypes = ParseStrings_(matches[7].Value); var temperatures = ParseNumbers_(matches[8].Value, int.Parse); - var voltages = ParseNumbers_(matches[9].Value, decimal.Parse); + var voltages = ParseNumbers_(matches[9].Value, x => decimal.Parse(x, CultureInfo.InvariantCulture)); var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); var hardwareStatus = new HardwareStatus() diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 741cb761..13862961 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; +using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -107,7 +108,7 @@ public async Task SetLearningMode(bool mode) /// public async Task MoveJoints(RobotJoints joints) { - await SendCommandAsync("MOVE_JOINTS", string.Join(',', joints)); + await SendCommandAsync("MOVE_JOINTS", string.Join(',', joints.Select(x => x.ToString(CultureInfo.InvariantCulture)))); await ReceiveAnswerAsync("MOVE_JOINTS"); } @@ -117,7 +118,7 @@ public async Task MoveJoints(RobotJoints joints) /// public async Task MovePose(PoseObject pose) { - await SendCommandAsync("MOVE_POSE", string.Join(',', pose)); + await SendCommandAsync("MOVE_POSE", string.Join(',', pose.Select(x => x.ToString(CultureInfo.InvariantCulture)))); await ReceiveAnswerAsync("MOVE_POSE"); } @@ -128,7 +129,7 @@ public async Task MovePose(PoseObject pose) /// public async Task ShiftPose(RobotAxis axis, float value) { - await SendCommandAsync("SHIFT_POSE", axis.ToString(), value.ToString()); + await SendCommandAsync("SHIFT_POSE", axis.ToString(), value.ToString(CultureInfo.InvariantCulture)); await ReceiveAnswerAsync("SHIFT_POSE"); } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs index 7c99f46b..79539448 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; namespace NiryoOneClient @@ -47,7 +48,7 @@ public PoseObject(float[] j) public static PoseObject Parse(string s) { - return new PoseObject(s.Split(",").Select(float.Parse).ToArray()); + return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture )).ToArray()); } public float X { get => _j[0]; set => _j[0] = value; } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs index 70d6b26f..f7dd9dc8 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; namespace NiryoOneClient @@ -47,7 +48,7 @@ public RobotJoints(float[] j) public static RobotJoints Parse(string s) { - return new RobotJoints(s.Split(",").Select(float.Parse).ToArray()); + return new RobotJoints(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); } public float J1 { get => _j[0]; set => _j[0] = value; } From 5a3a499bce76d0d050cacb6d85334ab2f20c9027 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Mon, 11 Nov 2019 00:09:08 +0100 Subject: [PATCH 21/38] Ignore coverage.json --- .../clients/csharp/.gitignore | 3 + .../csharp/NiryoOneClient.Tests/coverage.json | 1544 ----------------- 2 files changed, 3 insertions(+), 1544 deletions(-) delete mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json diff --git a/niryo_one_tcp_server/clients/csharp/.gitignore b/niryo_one_tcp_server/clients/csharp/.gitignore index e6452706..a80974fa 100644 --- a/niryo_one_tcp_server/clients/csharp/.gitignore +++ b/niryo_one_tcp_server/clients/csharp/.gitignore @@ -351,3 +351,6 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ + +# Coverage analysis +coverage.json diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json deleted file mode 100644 index 8a08c96c..00000000 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/coverage.json +++ /dev/null @@ -1,1544 +0,0 @@ -{ - "NiryoOneClient.dll": { - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs": { - "NiryoOneClient.HardwareStatus": { - "System.String NiryoOneClient.HardwareStatus::Strip_(System.String,System.Char,System.Char)": { - "Lines": { - "32": 26, - "33": 26, - "34": 0, - "35": 26, - "36": 0, - "37": 26, - "38": 26 - }, - "Branches": [ - { - "Line": 33, - "Offset": 13, - "EndOffset": 15, - "Path": 0, - "Ordinal": 0, - "Hits": 0 - }, - { - "Line": 33, - "Offset": 13, - "EndOffset": 21, - "Path": 1, - "Ordinal": 1, - "Hits": 26 - }, - { - "Line": 35, - "Offset": 33, - "EndOffset": 35, - "Path": 0, - "Ordinal": 2, - "Hits": 0 - }, - { - "Line": 35, - "Offset": 33, - "EndOffset": 41, - "Path": 1, - "Ordinal": 3, - "Hits": 26 - } - ] - }, - "System.String[] NiryoOneClient.HardwareStatus::ParseStrings_(System.String)": { - "Lines": { - "41": 4, - "42": 4, - "43": 28, - "44": 4 - }, - "Branches": [ - { - "Line": 43, - "Offset": 25, - "EndOffset": 27, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 43, - "Offset": 25, - "EndOffset": 50, - "Path": 1, - "Ordinal": 1, - "Hits": 4 - } - ] - }, - "T[] NiryoOneClient.HardwareStatus::ParseNumbers_(System.String,System.Func`2)": { - "Lines": { - "47": 6, - "48": 6, - "49": 42, - "50": 6 - }, - "Branches": [] - }, - "NiryoOneClient.HardwareStatus NiryoOneClient.HardwareStatus::Parse(System.String)": { - "Lines": { - "53": 2, - "54": 2, - "55": 2, - "57": 2, - "58": 0, - "59": 0, - "62": 2, - "63": 2, - "64": 2, - "65": 2, - "66": 2, - "67": 2, - "69": 2, - "70": 2, - "72": 2, - "73": 2, - "74": 2, - "76": 2, - "77": 2, - "78": 2, - "79": 2, - "80": 2, - "81": 2, - "82": 2, - "83": 2, - "84": 2, - "85": 2, - "86": 2, - "87": 2, - "88": 2, - "89": 2, - "90": 2, - "91": 2 - }, - "Branches": [ - { - "Line": 57, - "Offset": 37, - "EndOffset": 39, - "Path": 0, - "Ordinal": 0, - "Hits": 0 - }, - { - "Line": 57, - "Offset": 37, - "EndOffset": 51, - "Path": 1, - "Ordinal": 1, - "Hits": 2 - } - ] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs": { - "NiryoOneClient.NiryoOneClient": { - "System.Void NiryoOneClient.NiryoOneClient::Dispose()": { - "Lines": { - "66": 0, - "67": 0, - "68": 0, - "69": 0, - "70": 0, - "71": 0, - "72": 0, - "73": 0 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.NiryoOneClient::.ctor(System.String,System.Int32)": { - "Lines": { - "44": 0, - "45": 0, - "46": 0, - "47": 0, - "48": 0 - }, - "Branches": [] - } - }, - "NiryoOneClient.NiryoOneClient/d__6": { - "System.Void NiryoOneClient.NiryoOneClient/d__6::MoveNext()": { - "Lines": { - "51": 0, - "52": 0, - "53": 0, - "54": 0, - "55": 0, - "56": 0, - "58": 0, - "59": 0, - "60": 0, - "61": 0, - "62": 0, - "63": 0 - }, - "Branches": [ - { - "Line": 52, - "Offset": 34, - "EndOffset": 36, - "Path": 0, - "Ordinal": 0, - "Hits": 0 - }, - { - "Line": 52, - "Offset": 34, - "EndOffset": 67, - "Path": 1, - "Ordinal": 1, - "Hits": 0 - }, - { - "Line": 59, - "Offset": 134, - "EndOffset": 136, - "Path": 0, - "Ordinal": 2, - "Hits": 0 - }, - { - "Line": 59, - "Offset": 134, - "EndOffset": 204, - "Path": 1, - "Ordinal": 3, - "Hits": 0 - } - ] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs": { - "NiryoOneClient.NiryoOneConnection": { - "System.Void NiryoOneClient.NiryoOneConnection::.ctor(System.IO.TextReader,System.IO.TextWriter)": { - "Lines": { - "38": 28, - "39": 28, - "40": 28, - "41": 28, - "42": 28 - }, - "Branches": [] - } - }, - "NiryoOneClient.NiryoOneConnection/d__3": { - "System.Void NiryoOneClient.NiryoOneConnection/d__3::MoveNext()": { - "Lines": { - "45": 28, - "46": 28, - "47": 28, - "48": 28 - }, - "Branches": [ - { - "Line": 45, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 28 - }, - { - "Line": 46, - "Offset": 61, - "EndOffset": 130, - "Path": 1, - "Ordinal": 3, - "Hits": 28 - }, - { - "Line": 47, - "Offset": 167, - "EndOffset": 233, - "Path": 1, - "Ordinal": 5, - "Hits": 28 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__4": { - "System.Void NiryoOneClient.NiryoOneConnection/d__4::MoveNext()": { - "Lines": { - "51": 28, - "52": 28, - "53": 28 - }, - "Branches": [ - { - "Line": 52, - "Offset": 44, - "EndOffset": 110, - "Path": 1, - "Ordinal": 1, - "Hits": 28 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__5": { - "System.Void NiryoOneClient.NiryoOneConnection/d__5::MoveNext()": { - "Lines": { - "56": 28, - "58": 28, - "59": 23, - "61": 5, - "62": 28, - "63": 28 - }, - "Branches": [ - { - "Line": 58, - "Offset": 31, - "EndOffset": 33, - "Path": 0, - "Ordinal": 0, - "Hits": 23 - }, - { - "Line": 58, - "Offset": 31, - "EndOffset": 73, - "Path": 1, - "Ordinal": 1, - "Hits": 5 - }, - { - "Line": 62, - "Offset": 115, - "EndOffset": 181, - "Path": 1, - "Ordinal": 3, - "Hits": 28 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__6": { - "System.Void NiryoOneClient.NiryoOneConnection/d__6::MoveNext()": { - "Lines": { - "66": 28, - "67": 28, - "68": 28, - "69": 28, - "70": 28, - "71": 28, - "72": 1, - "73": 27, - "74": 27, - "75": 27, - "76": 2, - "78": 25, - "79": 6, - "81": 19, - "82": 25 - }, - "Branches": [ - { - "Line": 67, - "Offset": 39, - "EndOffset": 108, - "Path": 1, - "Ordinal": 1, - "Hits": 28 - }, - { - "Line": 71, - "Offset": 215, - "EndOffset": 217, - "Path": 0, - "Ordinal": 2, - "Hits": 1 - }, - { - "Line": 71, - "Offset": 215, - "EndOffset": 228, - "Path": 1, - "Ordinal": 3, - "Hits": 27 - }, - { - "Line": 75, - "Offset": 285, - "EndOffset": 287, - "Path": 0, - "Ordinal": 4, - "Hits": 2 - }, - { - "Line": 75, - "Offset": 285, - "EndOffset": 315, - "Path": 1, - "Ordinal": 5, - "Hits": 25 - }, - { - "Line": 78, - "Offset": 330, - "EndOffset": 332, - "Path": 0, - "Ordinal": 6, - "Hits": 6 - }, - { - "Line": 78, - "Offset": 330, - "EndOffset": 343, - "Path": 1, - "Ordinal": 7, - "Hits": 19 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__7": { - "System.Void NiryoOneClient.NiryoOneConnection/d__7::MoveNext()": { - "Lines": { - "89": 3, - "90": 3, - "91": 3, - "92": 2 - }, - "Branches": [ - { - "Line": 89, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 3 - }, - { - "Line": 90, - "Offset": 81, - "EndOffset": 150, - "Path": 1, - "Ordinal": 3, - "Hits": 3 - }, - { - "Line": 91, - "Offset": 187, - "EndOffset": 253, - "Path": 1, - "Ordinal": 5, - "Hits": 3 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__8": { - "System.Void NiryoOneClient.NiryoOneConnection/d__8::MoveNext()": { - "Lines": { - "99": 4, - "100": 4, - "101": 4, - "102": 2 - }, - "Branches": [ - { - "Line": 99, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 4 - }, - { - "Line": 100, - "Offset": 80, - "EndOffset": 149, - "Path": 1, - "Ordinal": 3, - "Hits": 4 - }, - { - "Line": 101, - "Offset": 186, - "EndOffset": 252, - "Path": 1, - "Ordinal": 5, - "Hits": 4 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__9": { - "System.Void NiryoOneClient.NiryoOneConnection/d__9::MoveNext()": { - "Lines": { - "109": 1, - "110": 1, - "111": 1, - "112": 1 - }, - "Branches": [ - { - "Line": 109, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 110, - "Offset": 77, - "EndOffset": 146, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 111, - "Offset": 183, - "EndOffset": 249, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__10": { - "System.Void NiryoOneClient.NiryoOneConnection/d__10::MoveNext()": { - "Lines": { - "119": 1, - "120": 1, - "121": 1, - "122": 1 - }, - "Branches": [ - { - "Line": 119, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 120, - "Offset": 77, - "EndOffset": 146, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 121, - "Offset": 183, - "EndOffset": 249, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__11": { - "System.Void NiryoOneClient.NiryoOneConnection/d__11::MoveNext()": { - "Lines": { - "130": 1, - "131": 1, - "132": 1, - "133": 1 - }, - "Branches": [ - { - "Line": 130, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 131, - "Offset": 95, - "EndOffset": 164, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 132, - "Offset": 201, - "EndOffset": 267, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__12": { - "System.Void NiryoOneClient.NiryoOneConnection/d__12::MoveNext()": { - "Lines": { - "140": 1, - "141": 1, - "142": 1, - "143": 1 - }, - "Branches": [ - { - "Line": 140, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 141, - "Offset": 75, - "EndOffset": 144, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 142, - "Offset": 181, - "EndOffset": 247, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__13": { - "System.Void NiryoOneClient.NiryoOneConnection/d__13::MoveNext()": { - "Lines": { - "149": 1, - "150": 1, - "151": 1, - "152": 1 - }, - "Branches": [ - { - "Line": 149, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 150, - "Offset": 80, - "EndOffset": 149, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 151, - "Offset": 186, - "EndOffset": 252, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__14": { - "System.Void NiryoOneClient.NiryoOneConnection/d__14::MoveNext()": { - "Lines": { - "158": 1, - "159": 1, - "160": 1, - "161": 1 - }, - "Branches": [ - { - "Line": 158, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 159, - "Offset": 101, - "EndOffset": 170, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 160, - "Offset": 207, - "EndOffset": 273, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__15": { - "System.Void NiryoOneClient.NiryoOneConnection/d__15::MoveNext()": { - "Lines": { - "167": 1, - "168": 1, - "169": 1, - "170": 1 - }, - "Branches": [ - { - "Line": 167, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 168, - "Offset": 101, - "EndOffset": 170, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 169, - "Offset": 207, - "EndOffset": 273, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__16": { - "System.Void NiryoOneClient.NiryoOneConnection/d__16::MoveNext()": { - "Lines": { - "176": 1, - "177": 1, - "178": 1, - "179": 1, - "180": 1 - }, - "Branches": [ - { - "Line": 176, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 177, - "Offset": 81, - "EndOffset": 150, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 178, - "Offset": 188, - "EndOffset": 259, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__17": { - "System.Void NiryoOneClient.NiryoOneConnection/d__17::MoveNext()": { - "Lines": { - "186": 1, - "187": 1, - "188": 1, - "189": 1 - }, - "Branches": [ - { - "Line": 186, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 187, - "Offset": 81, - "EndOffset": 150, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 188, - "Offset": 187, - "EndOffset": 253, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__18": { - "System.Void NiryoOneClient.NiryoOneConnection/d__18::MoveNext()": { - "Lines": { - "197": 1, - "198": 1, - "199": 1, - "200": 1 - }, - "Branches": [ - { - "Line": 197, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 198, - "Offset": 95, - "EndOffset": 164, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 199, - "Offset": 201, - "EndOffset": 267, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__19": { - "System.Void NiryoOneClient.NiryoOneConnection/d__19::MoveNext()": { - "Lines": { - "208": 1, - "209": 1, - "210": 1, - "211": 1 - }, - "Branches": [ - { - "Line": 208, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 209, - "Offset": 95, - "EndOffset": 164, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 210, - "Offset": 201, - "EndOffset": 267, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__20": { - "System.Void NiryoOneClient.NiryoOneConnection/d__20::MoveNext()": { - "Lines": { - "218": 1, - "219": 1, - "220": 1, - "221": 1 - }, - "Branches": [ - { - "Line": 218, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 219, - "Offset": 81, - "EndOffset": 150, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 220, - "Offset": 187, - "EndOffset": 253, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__21": { - "System.Void NiryoOneClient.NiryoOneConnection/d__21::MoveNext()": { - "Lines": { - "228": 1, - "229": 1, - "230": 1, - "231": 1 - }, - "Branches": [ - { - "Line": 228, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 229, - "Offset": 81, - "EndOffset": 150, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 230, - "Offset": 187, - "EndOffset": 253, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__22": { - "System.Void NiryoOneClient.NiryoOneConnection/d__22::MoveNext()": { - "Lines": { - "239": 1, - "240": 1, - "241": 1, - "242": 1 - }, - "Branches": [ - { - "Line": 239, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 240, - "Offset": 101, - "EndOffset": 170, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 241, - "Offset": 207, - "EndOffset": 273, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__23": { - "System.Void NiryoOneClient.NiryoOneConnection/d__23::MoveNext()": { - "Lines": { - "250": 1, - "251": 1, - "252": 1, - "253": 1 - }, - "Branches": [ - { - "Line": 250, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 251, - "Offset": 101, - "EndOffset": 170, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 252, - "Offset": 207, - "EndOffset": 273, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__24": { - "System.Void NiryoOneClient.NiryoOneConnection/d__24::MoveNext()": { - "Lines": { - "261": 1, - "262": 1, - "263": 1, - "264": 1 - }, - "Branches": [ - { - "Line": 261, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 262, - "Offset": 101, - "EndOffset": 170, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 263, - "Offset": 207, - "EndOffset": 273, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__25": { - "System.Void NiryoOneClient.NiryoOneConnection/d__25::MoveNext()": { - "Lines": { - "270": 1, - "271": 1, - "272": 1, - "273": 1, - "274": 1 - }, - "Branches": [ - { - "Line": 270, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 271, - "Offset": 60, - "EndOffset": 129, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 272, - "Offset": 167, - "EndOffset": 235, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__26": { - "System.Void NiryoOneClient.NiryoOneConnection/d__26::MoveNext()": { - "Lines": { - "280": 1, - "281": 1, - "282": 1, - "283": 1, - "284": 1 - }, - "Branches": [ - { - "Line": 280, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 281, - "Offset": 60, - "EndOffset": 129, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 282, - "Offset": 167, - "EndOffset": 235, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__27": { - "System.Void NiryoOneClient.NiryoOneConnection/d__27::MoveNext()": { - "Lines": { - "290": 1, - "291": 1, - "292": 1, - "293": 1, - "294": 1 - }, - "Branches": [ - { - "Line": 290, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 291, - "Offset": 60, - "EndOffset": 129, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 292, - "Offset": 167, - "EndOffset": 235, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/d__28": { - "System.Void NiryoOneClient.NiryoOneConnection/d__28::MoveNext()": { - "Lines": { - "300": 1, - "301": 1, - "302": 1, - "303": 1, - "304": 1 - }, - "Branches": [ - { - "Line": 300, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 301, - "Offset": 60, - "EndOffset": 129, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 302, - "Offset": 167, - "EndOffset": 235, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - } - ] - } - }, - "NiryoOneClient.NiryoOneConnection/<>c": { - "NiryoOneClient.DigitalPinObject NiryoOneClient.NiryoOneConnection/<>c::b__29_0(System.Text.RegularExpressions.Match)": { - "Lines": { - "317": 9 - }, - "Branches": [] - } - }, - "NiryoOneClient.NiryoOneConnection/d__29": { - "System.Void NiryoOneClient.NiryoOneConnection/d__29::MoveNext()": { - "Lines": { - "310": 1, - "311": 1, - "312": 1, - "314": 1, - "315": 1, - "318": 1 - }, - "Branches": [ - { - "Line": 310, - "Offset": 14, - "EndOffset": 25, - "Path": 0, - "Ordinal": 0, - "Hits": 1 - }, - { - "Line": 311, - "Offset": 60, - "EndOffset": 129, - "Path": 1, - "Ordinal": 3, - "Hits": 1 - }, - { - "Line": 312, - "Offset": 167, - "EndOffset": 238, - "Path": 1, - "Ordinal": 5, - "Hits": 1 - }, - { - "Line": 317, - "Offset": 321, - "EndOffset": 323, - "Path": 0, - "Ordinal": 6, - "Hits": 1 - }, - { - "Line": 317, - "Offset": 321, - "EndOffset": 346, - "Path": 1, - "Ordinal": 7, - "Hits": 1 - } - ] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs": { - "NiryoOneClient.NiryoOneException": { - "System.String NiryoOneClient.NiryoOneException::get_Reason()": { - "Lines": { - "35": 3 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.NiryoOneException::.ctor(System.String)": { - "Lines": { - "30": 3, - "31": 3, - "32": 3, - "33": 3 - }, - "Branches": [] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs": { - "NiryoOneClient.PoseObject": { - "NiryoOneClient.PoseObject NiryoOneClient.PoseObject::Parse(System.String)": { - "Lines": { - "49": 1, - "50": 1, - "51": 1 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_X()": { - "Lines": { - "53": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_Y()": { - "Lines": { - "54": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_Z()": { - "Lines": { - "55": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_Roll()": { - "Lines": { - "56": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_Pitch()": { - "Lines": { - "57": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.PoseObject::get_Yaw()": { - "Lines": { - "58": 0 - }, - "Branches": [] - }, - "System.Collections.Generic.IEnumerator`1 NiryoOneClient.PoseObject::GetEnumerator()": { - "Lines": { - "61": 2, - "62": 2, - "63": 2 - }, - "Branches": [] - }, - "System.Collections.IEnumerator NiryoOneClient.PoseObject::System.Collections.IEnumerable.GetEnumerator()": { - "Lines": { - "66": 0, - "67": 0, - "68": 0 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.PoseObject::.ctor(System.Single,System.Single,System.Single,System.Single,System.Single,System.Single)": { - "Lines": { - "33": 2, - "35": 0, - "36": 0, - "37": 0, - "38": 0 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.PoseObject::.ctor(System.Single[])": { - "Lines": { - "40": 2, - "41": 2, - "42": 2, - "43": 0, - "45": 2, - "46": 2 - }, - "Branches": [ - { - "Line": 42, - "Offset": 31, - "EndOffset": 33, - "Path": 0, - "Ordinal": 0, - "Hits": 0 - }, - { - "Line": 42, - "Offset": 31, - "EndOffset": 49, - "Path": 1, - "Ordinal": 1, - "Hits": 2 - } - ] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs": { - "NiryoOneClient.RobotJoints": { - "NiryoOneClient.RobotJoints NiryoOneClient.RobotJoints::Parse(System.String)": { - "Lines": { - "49": 1, - "50": 1, - "51": 1 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J1()": { - "Lines": { - "53": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J2()": { - "Lines": { - "54": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J3()": { - "Lines": { - "55": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J4()": { - "Lines": { - "56": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J5()": { - "Lines": { - "57": 0 - }, - "Branches": [] - }, - "System.Single NiryoOneClient.RobotJoints::get_J6()": { - "Lines": { - "58": 0 - }, - "Branches": [] - }, - "System.Collections.Generic.IEnumerator`1 NiryoOneClient.RobotJoints::GetEnumerator()": { - "Lines": { - "61": 2, - "62": 2, - "63": 2 - }, - "Branches": [] - }, - "System.Collections.IEnumerator NiryoOneClient.RobotJoints::System.Collections.IEnumerable.GetEnumerator()": { - "Lines": { - "66": 0, - "67": 0, - "68": 0 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.RobotJoints::.ctor(System.Single,System.Single,System.Single,System.Single,System.Single,System.Single)": { - "Lines": { - "33": 2, - "35": 0, - "36": 0, - "37": 0, - "38": 0 - }, - "Branches": [] - }, - "System.Void NiryoOneClient.RobotJoints::.ctor(System.Single[])": { - "Lines": { - "40": 2, - "41": 2, - "42": 2, - "43": 0, - "45": 2, - "46": 2 - }, - "Branches": [ - { - "Line": 42, - "Offset": 31, - "EndOffset": 33, - "Path": 0, - "Ordinal": 0, - "Hits": 0 - }, - { - "Line": 42, - "Offset": 31, - "EndOffset": 49, - "Path": 1, - "Ordinal": 1, - "Hits": 2 - } - ] - } - } - }, - "/Users/rickard/git/niryo_one_ros/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs": { - "NiryoOneClient.DigitalPinObject": { - "NiryoOneClient.DigitalPinObject NiryoOneClient.DigitalPinObject::Parse(System.String)": { - "Lines": { - "56": 8, - "57": 8, - "58": 0, - "60": 8, - "62": 8, - "63": 8, - "64": 8, - "65": 8, - "66": 8, - "67": 8, - "68": 8, - "69": 8 - }, - "Branches": [ - { - "Line": 57, - "Offset": 9, - "EndOffset": 11, - "Path": 0, - "Ordinal": 0, - "Hits": 8 - }, - { - "Line": 57, - "Offset": 9, - "EndOffset": 24, - "Path": 1, - "Ordinal": 1, - "Hits": 0 - }, - { - "Line": 57, - "Offset": 27, - "EndOffset": 29, - "Path": 0, - "Ordinal": 2, - "Hits": 0 - }, - { - "Line": 57, - "Offset": 27, - "EndOffset": 35, - "Path": 1, - "Ordinal": 3, - "Hits": 8 - } - ] - } - } - } - } -} \ No newline at end of file From c3490d3a3bb613ecab7448fa1929e80ed6a0d34f Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Mon, 11 Nov 2019 00:10:25 +0100 Subject: [PATCH 22/38] Add enough comments to be warning-free --- .../clients/csharp/Examples/Program.cs | 3 +- .../csharp/NiryoOneClient/HardwareStatus.cs | 53 ++++++++++++++- .../csharp/NiryoOneClient/NiryoOneClient.cs | 26 +++++++ .../NiryoOneClient/NiryoOneConnection.cs | 28 +++++++- .../NiryoOneClient/NiryoOneException.cs | 12 +++- .../csharp/NiryoOneClient/PoseObject.cs | 67 ++++++++++++++++++- .../csharp/NiryoOneClient/RobotJoints.cs | 34 ++++++++++ .../clients/csharp/NiryoOneClient/RobotPin.cs | 54 ++++++++++++++- .../csharp/NiryoOneClient/RobotTool.cs | 8 +++ niryo_one_tcp_server/clients/csharp/README.md | 2 +- 10 files changed, 278 insertions(+), 9 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs index 69f979f6..da710b4b 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Program.cs +++ b/niryo_one_tcp_server/clients/csharp/Examples/Program.cs @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; +using System.Globalization; using System.Linq; using System.Threading.Tasks; using NiryoOneClient; @@ -48,7 +49,7 @@ public static async Task Main(string[] args) PoseObject initialPose = null; if (args.Length == 6) - initialPose = new PoseObject(args.Select(f => float.Parse(f, CultureInfo.InvariantCulture).ToArray()); + initialPose = new PoseObject(args.Select(f => float.Parse(f, CultureInfo.InvariantCulture)).ToArray()); Console.WriteLine("Calibrating..."); await niryo.Calibrate(CalibrateMode.AUTO); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index c152cb78..76e5008c 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -27,6 +27,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// A representation of the status of several system components in the Niryo One. + /// public class HardwareStatus { private static string Strip_(string s, char prefix, char suffix) @@ -50,6 +53,11 @@ private static T[] ParseNumbers_(string s, Func parser) return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); } + /// + /// Parse a string representation of the hardware status in the format of the tcp server + /// + /// The string representation + /// A parsed object public static HardwareStatus Parse(string data) { var regex = new System.Text.RegularExpressions.Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); @@ -91,16 +99,59 @@ public static HardwareStatus Parse(string data) return hardwareStatus; } + /// + /// The core temperature of the Raspberry Pi + /// public int RpiTemperature; + + /// + /// The hardware version + /// public int HardwareVersion; + + /// + /// Whether a connection to the robot is up + /// public bool ConnectionUp; + + /// + /// The current error message + /// public string ErrorMessage; + + /// + /// Whether the robot needs a calibartion to perform moves + /// public int CalibrationNeeded; + + /// + /// Whether a calibration is in progress + /// public bool CalibrationInProgress; + + /// + /// The names of the connected motors + /// public string[] MotorNames; + + /// + /// The model names of the connected motors + /// public string[] MotorTypes; + + /// + /// The temperatures in degrees celsius of the connected motors + /// public int[] Temperatures; + + /// + /// The voltages applied to the connected motors + /// public decimal[] Voltages; + + /// + /// The number of hardware errors on the respective motors + /// public int[] HardwareErrors; } -} \ No newline at end of file +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index c4723a61..abc2e8ba 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -27,12 +27,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// The type of calibration to be performed + /// public enum CalibrateMode { + /// + /// Automatic calibration where the robot performs a set of move to establish calibration + /// AUTO, + + /// + /// Manual calibration where the robot is manually positioned in the calibration pose and that + /// pose is used to establish calibration + /// MANUAL } + /// + /// A client capable of connecting to the tcp server of a Niryo One robotic arm + /// public class NiryoOneClient : IDisposable { private TcpClient _client; @@ -41,12 +55,21 @@ public class NiryoOneClient : IDisposable private NetworkStream _stream; private NiryoOneConnection _connection; + /// + /// Construct a client + /// + /// The server address, ip or hostname + /// The port number, defaults to 40001 public NiryoOneClient(string server, int port = 40001) { _server = server; _port = port; } + /// + /// Create a connection to the robot + /// + /// A NiryoOneConnection object used for sending commands to the robot public async Task Connect() { if (_client != null) @@ -62,6 +85,9 @@ public async Task Connect() return _connection; } + /// + /// Dispose the object + /// public void Dispose() { _stream.Close(); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 13862961..ea589309 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -31,28 +31,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// A connection object allowing sending commands to the tcp server on a Niryo One robotic arm + /// public class NiryoOneConnection { private readonly TextWriter _textWriter; private readonly TextReader _textReader; + /// + /// Construct a connection object + /// + /// A stream reader used for getting responses + /// A stream writer used for sending commands public NiryoOneConnection(TextReader streamReader, TextWriter streamWriter) { _textWriter = streamWriter; _textReader = streamReader; } + /// + /// Send a command to the tcp server + /// + /// A command internal async Task WriteLineAsync(string s) { await _textWriter.WriteLineAsync(s); await _textWriter.FlushAsync(); } + /// + /// Read a response line from the tcp server + /// + /// The line read internal async Task ReadLineAsync() { return await _textReader.ReadLineAsync(); } + /// + /// Send a command to the tcp server + /// + /// The type of command + /// The arguments protected async Task SendCommandAsync(string command_type, params string[] args) { string cmd; @@ -63,6 +84,11 @@ protected async Task SendCommandAsync(string command_type, params string[] args) await WriteLineAsync(cmd); } + /// + /// Receive an answer fromt the tcp server related to a previously sent command + /// + /// The command for which a response is expected + /// The data portion of the desponse protected async Task ReceiveAnswerAsync(string command_type) { var result = await ReadLineAsync(); @@ -318,4 +344,4 @@ public async Task GetDigitalIOState() return matches.Select(m => DigitalPinObject.Parse(m.Value)).ToArray(); } } -} \ No newline at end of file +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs index 52da1c61..76f6304a 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneException.cs @@ -25,13 +25,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// An exception representing an error from the Niryo One tcp server + /// public class NiryoOneException : Exception { + /// + /// Construct an exception with the specified reason + /// + /// A description of the error public NiryoOneException(string reason) { Reason = reason; } + /// + /// A description of the error + /// public string Reason { get; } } -} \ No newline at end of file +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs index 79539448..5e67c9ab 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -29,15 +29,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// A representation of a cartesian position of the robotic arm in 6 dimensions. + /// For a description of the coordinate system, see REP-0103. + /// public class PoseObject : IEnumerable { private float[] _j = new float[6]; + /// + /// Construct a PoseObject from explicit coordinates. + /// For a description of the coordinate system, see REP-0103. + /// + /// X position in meters + /// Y position in meters + /// Z position in meters + /// Rotation about the fixed X axis in radians + /// Rotation about the fixed Y axis in radians + /// Rotation about the fixed Z axis in radians public PoseObject(float x, float y, float z, float roll, float pitch, float yaw) { _j = new[] { x, y, z, roll, pitch, yaw }; } + /// + /// Construct a PoseObject from a coordinate array. + /// For a description of the coordinate system, see REP-0103. + /// + /// An array of the coordinates, in order X, Y, Z, roll, pitch, yaw public PoseObject(float[] j) { if (j.Length != 6) @@ -46,36 +65,80 @@ public PoseObject(float[] j) _j = j; } + /// + /// Parse a string representation of a pose in the format of the tcp server + /// + /// The string representation + /// A parsed object public static PoseObject Parse(string s) { - return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture )).ToArray()); + return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); } + /// + /// The X position in meters. + /// public float X { get => _j[0]; set => _j[0] = value; } + + /// + /// The Y position in meters. + /// public float Y { get => _j[1]; set => _j[1] = value; } + + /// + /// The Z position in meters. + /// public float Z { get => _j[2]; set => _j[2] = value; } + + /// + /// The roll, i.e. the rotation around the fixed X axis in radians. + /// public float Roll { get => _j[3]; set => _j[3] = value; } + + /// + /// The pitch, i.e. the rotation around the fixed Y axis in radians. + /// public float Pitch { get => _j[4]; set => _j[4] = value; } + + /// + /// The yaw, i.e. the rotation around the fixed Z axis in radians. + /// public float Yaw { get => _j[5]; set => _j[5] = value; } + /// + /// Returns an enumerator which iterates through the collection + /// public IEnumerator GetEnumerator() { return ((IEnumerable)_j).GetEnumerator(); } + /// + /// Returns an enumerator which iterates through the collection + /// IEnumerator IEnumerable.GetEnumerator() { return _j.GetEnumerator(); } } + /// + /// One of the 6 axes. + /// For a description of the coordinate system, see REP-0103. + /// public enum RobotAxis { + /// The X axis X, + /// The Y axis Y, + /// The Z axis Z, + /// The roll, i.e. the rotation around the fixed X axis in radians. ROLL, + /// The pitch, i.e. the rotation around the fixed Y axis in radians. PITCH, + /// The yaw, i.e. the rotation around the fixed Z axis in radians. YAW } -} \ No newline at end of file +} diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs index f7dd9dc8..20159d86 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -29,15 +29,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// A representation of the position of the joints in a Niryo One robotic arm + /// public class RobotJoints : IEnumerable { private float[] _j = new float[6]; + /// + /// Construct an object from the 6 joint values + /// + /// The first joint rotation in radians + /// The second joint rotation in radians + /// The third joint rotation in radians + /// The fourth joint rotation in radians + /// The fifth joint rotation in radians + /// The sixth joint rotation in radians public RobotJoints(float j1, float j2, float j3, float j4, float j5, float j6) { _j = new[] { j1, j2, j3, j4, j5, j6 }; } + /// + /// Construct an object from 6 joint values + /// + /// An array of the 6 joint rotations in radians public RobotJoints(float[] j) { if (j.Length != 6) @@ -46,23 +62,41 @@ public RobotJoints(float[] j) _j = j; } + + /// + /// Parse a string representation of a joint configuration in the format of the tcp server + /// + /// The string representation + /// A parsed object public static RobotJoints Parse(string s) { return new RobotJoints(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); } + /// The value of the first joint, in radians public float J1 { get => _j[0]; set => _j[0] = value; } + /// The value of the second joint, in radians public float J2 { get => _j[1]; set => _j[1] = value; } + /// The value of the third joint, in radians public float J3 { get => _j[2]; set => _j[2] = value; } + /// The value of the fourth joint, in radians public float J4 { get => _j[3]; set => _j[3] = value; } + /// The value of the fifth joint, in radians public float J5 { get => _j[4]; set => _j[4] = value; } + /// The value of the sixth joint, in radians public float J6 { get => _j[5]; set => _j[5] = value; } + /// + /// Returns an enumerator that iterates through the colleection + /// public IEnumerator GetEnumerator() { return ((IEnumerable)_j).GetEnumerator(); } + /// + /// Returns an enumerator that iterates through the colleection + /// IEnumerator IEnumerable.GetEnumerator() { return _j.GetEnumerator(); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs index 021a4d0a..af235f0f 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -25,33 +25,83 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// An enumeration of the digital GPIO pins on the Niryo One robotic arm + /// public enum RobotPin { + /// The pin labeled 1A GPIO_1A, + /// The pin labeled 1B GPIO_1B, + /// The pin labeled 1C GPIO_1C, + /// The pin labeled 2A GPIO_2A, + /// The pin labeled 2B GPIO_2B, + /// The pin labeled 2C GPIO_2C } + /// + /// The configuration mode of a GPIO pin - input or output + /// public enum PinMode { - OUTPUT = 0, INPUT = 1 + /// + /// The pin is configured as an output + /// + OUTPUT = 0, + /// + /// The pin is configured as an input + /// + INPUT = 1 } + /// + /// The state of a pin - high or low + /// public enum DigitalState { - LOW = 0, HIGH = 1 + /// + /// The pin is low + /// + LOW = 0, + /// + /// The pin is high + /// + HIGH = 1 } + /// + /// A representation of the state of a pin + /// public class DigitalPinObject { + /// + /// The internal pin id + /// public int PinId; + /// + /// The user-readable name of a pin + /// public string Name; + /// + /// Whether the pin is configured for output or input + /// public PinMode Mode; + /// + /// Whether the pin is low or high + /// public DigitalState State; + + /// + /// Parse a string representation of a digital pin state in the format of the tcp server + /// + /// The string representation + /// A parsed object public static DigitalPinObject Parse(string s) { if (!s.StartsWith('[') || !s.EndsWith(']')) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs index 974bc743..2826983a 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotTool.cs @@ -23,12 +23,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { + /// + /// An enumeration of the known types of tools that can be connected to the Niryo One robotic arm + /// public enum RobotTool { + /// The first type of gripper GRIPPER_1, + /// The second type of gripper GRIPPER_2, + /// The third type of gripper GRIPPER_3, + /// A vacuum pump VACUUM_PUMP_1, + /// An electromagnet ELECTROMAGNET_1 } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/README.md b/niryo_one_tcp_server/clients/csharp/README.md index 59185db8..41e92990 100644 --- a/niryo_one_tcp_server/clients/csharp/README.md +++ b/niryo_one_tcp_server/clients/csharp/README.md @@ -14,7 +14,7 @@ Port of the server: 40001 See the [Examples](Examples) folder for existing scripts. -## Functions available +## Functions available After running `dotnet publish`, xml documentation will be generated at [NiryoOneClient](NiryoOneClient/bin/Debug/netcoreapp3.0/publish/NiryoOneClient.xml). From b8a541a00764ff2805c8269853b95d4140224f04 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Mon, 11 Nov 2019 22:49:51 +0100 Subject: [PATCH 23/38] Don't require newlines after robot responses --- .../clients/csharp/.gitignore | 1 + .../clients/csharp/.vscode/tasks.json | 4 +- .../NiryoOneConnectionTest.cs | 153 +++++++++++------- .../csharp/NiryoOneClient/HardwareStatus.cs | 4 +- .../NiryoOneClient/NiryoOneConnection.cs | 47 ++++-- .../NiryoOneClient/Properties/AssemblyInfo.cs | 3 + 6 files changed, 137 insertions(+), 75 deletions(-) create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/Properties/AssemblyInfo.cs diff --git a/niryo_one_tcp_server/clients/csharp/.gitignore b/niryo_one_tcp_server/clients/csharp/.gitignore index a80974fa..04e1705d 100644 --- a/niryo_one_tcp_server/clients/csharp/.gitignore +++ b/niryo_one_tcp_server/clients/csharp/.gitignore @@ -143,6 +143,7 @@ _TeamCity* # Visual Studio code coverage results *.coverage *.coveragexml +lcov.info # NCrunch _NCrunch_* diff --git a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json index 974c4441..2ac6968c 100644 --- a/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json +++ b/niryo_one_tcp_server/clients/csharp/.vscode/tasks.json @@ -55,7 +55,9 @@ "${workspaceFolder}/NiryoOneClient.sln", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary", - "/p:CollectCoverage=true" + "/p:CollectCoverage=true", + "/p:CoverletOutputFormat=lcov", + "/p:CoverletOutput=./lcov.info" ], "problemMatcher": "$msCompile" }, diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index afe2ef31..8244b2ef 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -21,8 +21,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using System; using System.IO; using System.Linq; +using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; @@ -32,22 +35,49 @@ namespace NiryoOneClient.Tests [TestClass] public class NiryoOneConnectionTest { - private TextReader _streamReader; + private class MyTextReader : TextReader { + public string[] Values = null; + private int _i; + + public MyTextReader() + { + _i = 0; + } + +#pragma warning disable 1998 + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_i >= Values.Length) + throw new SocketException(); + var value = Values[_i]; + _i++; + new Span(value.ToArray()).CopyTo(buffer.Span); + return value.Length; + } + } +#pragma warning restore 1998 + + private MyTextReader _streamReader; private TextWriter _streamWriter; private NiryoOneConnection _connection; public NiryoOneConnectionTest() { - _streamReader = Substitute.For(); + _streamReader = new MyTextReader(); _streamWriter = Substitute.For(); _connection = new NiryoOneConnection(_streamReader, _streamWriter); } + private void SetupReadLineAsyncReturn_(params string[] values) + { + _streamReader.Values = values; + } + [TestMethod] public async Task Calibrate_SuccessfulAuto_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + SetupReadLineAsyncReturn_("CALIBRATE:OK"); await _connection.Calibrate(CalibrateMode.AUTO); await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); } @@ -55,7 +85,7 @@ public async Task Calibrate_SuccessfulAuto_Works() [TestMethod] public async Task Calibrate_SuccessfulManual_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:OK\n")); + SetupReadLineAsyncReturn_("CALIBRATE:OK\n"); await _connection.Calibrate(CalibrateMode.MANUAL); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); } @@ -63,7 +93,7 @@ public async Task Calibrate_SuccessfulManual_Works() [TestMethod] public async Task Calibrate_Failure_Throws() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("CALIBRATE:KO,\"Sucks to be sucky\"")); + SetupReadLineAsyncReturn_("CALIBRATE:KO,\"Sucks to be sucky\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); @@ -72,7 +102,7 @@ public async Task Calibrate_Failure_Throws() [TestMethod] public async Task SetLearningMode_True_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + SetupReadLineAsyncReturn_("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(true); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } @@ -80,7 +110,7 @@ public async Task SetLearningMode_True_Works() [TestMethod] public async Task SetLearningMode_False_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:OK")); + SetupReadLineAsyncReturn_("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(false); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } @@ -88,7 +118,7 @@ public async Task SetLearningMode_False_Works() [TestMethod] public async Task SetLearningMode_WrongResponse_Throws() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("COCO:OK")); + SetupReadLineAsyncReturn_("COCO:OK"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("Wrong command response received.", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -97,7 +127,7 @@ public async Task SetLearningMode_WrongResponse_Throws() [TestMethod] public async Task SetLearningMode_ErrorResponse_Throws() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_LEARNING_MODE:KO,\"No good\"")); + SetupReadLineAsyncReturn_("SET_LEARNING_MODE:KO,\"No good\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("No good", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -106,7 +136,7 @@ public async Task SetLearningMode_ErrorResponse_Throws() [TestMethod] public async Task MoveJoints_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_JOINTS:OK")); + SetupReadLineAsyncReturn_("MOVE_JOINTS:OK"); await _connection.MoveJoints(new RobotJoints(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -116,7 +146,7 @@ await _connection.MoveJoints(new RobotJoints(new[] { [TestMethod] public async Task MovePose_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("MOVE_POSE:OK")); + SetupReadLineAsyncReturn_("MOVE_POSE:OK"); await _connection.MovePose(new PoseObject(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -126,7 +156,7 @@ await _connection.MovePose(new PoseObject(new[] { [TestMethod] public async Task ShiftPose_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SHIFT_POSE:OK")); + SetupReadLineAsyncReturn_("SHIFT_POSE:OK"); await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); } @@ -134,7 +164,7 @@ public async Task ShiftPose_Sample_SendsCorrectly() [TestMethod] public async Task SetArmMaxVelocity_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_ARM_MAX_VELOCITY:OK")); + SetupReadLineAsyncReturn_("SET_ARM_MAX_VELOCITY:OK"); await _connection.SetArmMaxVelocity(50); await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); } @@ -142,7 +172,7 @@ public async Task SetArmMaxVelocity_Sample_SendsCorrectly() [TestMethod] public async Task EnableJoystick_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("ENABLE_JOYSTICK:OK")); + SetupReadLineAsyncReturn_("ENABLE_JOYSTICK:OK"); await _connection.EnableJoystick(false); await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); } @@ -150,7 +180,7 @@ public async Task EnableJoystick_Sample_SendsCorrectly() [TestMethod] public async Task SetPinMode_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("SET_PIN_MODE:OK")); + SetupReadLineAsyncReturn_("SET_PIN_MODE:OK"); await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); } @@ -158,9 +188,7 @@ public async Task SetPinMode_Sample_SendsCorrectly() [TestMethod] public async Task DigitalWrite_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "DIGITAL_WRITE:OK" - )); + SetupReadLineAsyncReturn_("DIGITAL_WRITE:OK"); await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); } @@ -168,9 +196,7 @@ public async Task DigitalWrite_Sample_SendsCorrectly() [TestMethod] public async Task DigitalRead_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "DIGITAL_READ:OK,HIGH" - )); + SetupReadLineAsyncReturn_("DIGITAL_READ:OK,HIGH"); await _connection.DigitalRead(RobotPin.GPIO_1A); await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); } @@ -178,9 +204,7 @@ public async Task DigitalRead_Sample_SendsCorrectly() [TestMethod] public async Task ChangeTool_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "CHANGE_TOOL:OK" - )); + SetupReadLineAsyncReturn_("CHANGE_TOOL:OK"); await _connection.ChangeTool(RobotTool.GRIPPER_2); await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); } @@ -188,9 +212,7 @@ public async Task ChangeTool_Sample_SendsCorrectly() [TestMethod] public async Task OpenGripper_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "OPEN_GRIPPER:OK" - )); + SetupReadLineAsyncReturn_("OPEN_GRIPPER:OK"); await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); } @@ -198,9 +220,7 @@ public async Task OpenGripper_Sample_SendsCorrectly() [TestMethod] public async Task CloseGripper_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "CLOSE_GRIPPER:OK" - )); + SetupReadLineAsyncReturn_("CLOSE_GRIPPER:OK"); await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); } @@ -208,9 +228,7 @@ public async Task CloseGripper_Sample_SendsCorrectly() [TestMethod] public async Task PullAirVacuumPump_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "PULL_AIR_VACUUM_PUMP:OK" - )); + SetupReadLineAsyncReturn_("PULL_AIR_VACUUM_PUMP:OK"); await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -218,9 +236,7 @@ public async Task PullAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task PushAirVacuumPump_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "PUSH_AIR_VACUUM_PUMP:OK" - )); + SetupReadLineAsyncReturn_("PUSH_AIR_VACUUM_PUMP:OK"); await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -228,9 +244,7 @@ public async Task PushAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task SetupElectromagnet_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "SETUP_ELECTROMAGNET:OK" - )); + SetupReadLineAsyncReturn_("SETUP_ELECTROMAGNET:OK"); await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -238,9 +252,7 @@ public async Task SetupElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task ActivateElectromagnet_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "ACTIVATE_ELECTROMAGNET:OK" - )); + SetupReadLineAsyncReturn_("ACTIVATE_ELECTROMAGNET:OK"); await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -248,9 +260,7 @@ public async Task ActivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task DeactivateElectromagnet_Sample_SendsCorrectly() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "DEACTIVATE_ELECTROMAGNET:OK" - )); + SetupReadLineAsyncReturn_("DEACTIVATE_ELECTROMAGNET:OK"); await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); } @@ -258,9 +268,7 @@ public async Task DeactivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task GetJoints_Sample_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0" - )); + SetupReadLineAsyncReturn_("GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0"); var joints = await _connection.GetJoints(); CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); } @@ -268,9 +276,7 @@ public async Task GetJoints_Sample_Works() [TestMethod] public async Task GetPose_Sample_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12" - )); + SetupReadLineAsyncReturn_("GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12"); var pose = await _connection.GetPose(); CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, pose.ToArray()); @@ -279,9 +285,7 @@ public async Task GetPose_Sample_Works() [TestMethod] public async Task GetHardwareStatus_Sample_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)" - )); + SetupReadLineAsyncReturn_("GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)"); var status = await _connection.GetHardwareStatus(); Assert.AreEqual(59, status.RpiTemperature); Assert.AreEqual(2, status.HardwareVersion); @@ -299,7 +303,7 @@ public async Task GetHardwareStatus_Sample_Works() [TestMethod] public async Task GetLearningMode_Sample_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult("GET_LEARNING_MODE:OK,FALSE")); + SetupReadLineAsyncReturn_("GET_LEARNING_MODE:OK,FALSE"); var mode = await _connection.GetLearningMode(); await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); Assert.AreEqual(false, mode); @@ -308,9 +312,7 @@ public async Task GetLearningMode_Sample_Works() [TestMethod] public async Task GetDigitalIOState_Sample_Works() { - _streamReader.ReadLineAsync().Returns(Task.FromResult( - "GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]" - )); + SetupReadLineAsyncReturn_("GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]"); var state = await _connection.GetDigitalIOState(); Assert.AreEqual(2, state[0].PinId); Assert.AreEqual("1A", state[0].Name); @@ -352,5 +354,40 @@ public async Task GetDigitalIOState_Sample_Works() Assert.AreEqual(PinMode.OUTPUT, state[7].Mode); Assert.AreEqual(DigitalState.LOW, state[7].State); } + + [TestMethod] + public async Task ReceiveAnswerAsync_GroupedInOneRead_Works() { + SetupReadLineAsyncReturn_("HEJ:OKBLA:OKMERP:OK"); + var a1 = await _connection.ReceiveAnswerAsync("HEJ"); + Assert.AreEqual("", a1); + var a2 = await _connection.ReceiveAnswerAsync("BLA"); + Assert.AreEqual("", a2); + var a3 = await _connection.ReceiveAnswerAsync("MERP"); + Assert.AreEqual("", a3); + } + + [TestMethod] + public async Task ReceiveAnswerAsync_SplitInMultipleReads_Works() + { + SetupReadLineAsyncReturn_("HEJ:O", "KBLA:", "OKMERP", ":OK"); + var a1 = await _connection.ReceiveAnswerAsync("HEJ"); + Assert.AreEqual("", a1); + var a2 = await _connection.ReceiveAnswerAsync("BLA"); + Assert.AreEqual("", a2); + var a3 = await _connection.ReceiveAnswerAsync("MERP"); + Assert.AreEqual("", a3); + } + + [TestMethod] + public async Task ReceiveAnswerAsync_ArbitraryWhitespaceBetween_Works() + { + SetupReadLineAsyncReturn_("HEJ:OK\n\t BLA:OK \t\r\nMERP:OK\r\n"); + var a1 = await _connection.ReceiveAnswerAsync("HEJ"); + Assert.AreEqual("", a1); + var a2 = await _connection.ReceiveAnswerAsync("BLA"); + Assert.AreEqual("", a2); + var a3 = await _connection.ReceiveAnswerAsync("MERP"); + Assert.AreEqual("", a3); + } } } \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index 76e5008c..8b194e63 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -140,7 +140,7 @@ public static HardwareStatus Parse(string data) public string[] MotorTypes; /// - /// The temperatures in degrees celsius of the connected motors + /// The temperatures in degrees celcius of the connected motors /// public int[] Temperatures; @@ -154,4 +154,4 @@ public static HardwareStatus Parse(string data) /// public int[] HardwareErrors; } -} +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index ea589309..aa512064 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -61,12 +62,15 @@ internal async Task WriteLineAsync(string s) } /// - /// Read a response line from the tcp server + /// Read response data from the tcp server /// - /// The line read - internal async Task ReadLineAsync() + /// The string read + internal async Task ReadAsync() { - return await _textReader.ReadLineAsync(); + const int blocksize = 512; + Memory memory = new Memory(new char[blocksize]); + var count = await _textReader.ReadAsync(memory); + return new string(memory.Span.Slice(0, count).ToArray()); } /// @@ -84,15 +88,29 @@ protected async Task SendCommandAsync(string command_type, params string[] args) await WriteLineAsync(cmd); } + private string _stringBuf = ""; + /// /// Receive an answer fromt the tcp server related to a previously sent command /// /// The command for which a response is expected - /// The data portion of the desponse - protected async Task ReceiveAnswerAsync(string command_type) + /// Optionally, the regular expression that the successful response arguments + /// are supposed to match + /// The data portion of the response + internal async Task ReceiveAnswerAsync(string command_type, string regex="") { - var result = await ReadLineAsync(); - result = result.TrimEnd('\n'); + var fullRegex = new Regex($"^[A-Z_]+:(OK{regex}|KO,\"[^\"]*\")"); + string s = _stringBuf; + var sb = new StringBuilder(s); + while (!fullRegex.IsMatch(s)) + { + sb.Append(await ReadAsync()); + s = sb.ToString(); + } + var match = fullRegex.Match(s); + var result = match.Value.Trim(); + _stringBuf = s.Substring(match.Index + match.Length).TrimStart(); + var colonSplit = result.Split(':', 2); var cmd = colonSplit[0]; if (cmd != command_type) @@ -202,7 +220,7 @@ public async Task DigitalWrite(RobotPin pin, DigitalState state) public async Task DigitalRead(RobotPin pin) { await SendCommandAsync("DIGITAL_READ", pin.ToString()); - var state = await ReceiveAnswerAsync("DIGITAL_READ"); + var state = await ReceiveAnswerAsync("DIGITAL_READ", ",(0|1|HIGH|LOW)"); return (DigitalState)Enum.Parse(typeof(DigitalState), state); } @@ -296,7 +314,7 @@ public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) public async Task GetJoints() { await SendCommandAsync("GET_JOINTS"); - var joints = await ReceiveAnswerAsync("GET_JOINTS"); + var joints = await ReceiveAnswerAsync("GET_JOINTS", "(, *[-0-9.e]+){6}"); return RobotJoints.Parse(joints); } @@ -306,7 +324,7 @@ public async Task GetJoints() public async Task GetPose() { await SendCommandAsync("GET_POSE"); - var pose = await ReceiveAnswerAsync("GET_POSE"); + var pose = await ReceiveAnswerAsync("GET_POSE", "(, *[-0-9.e]+){6}"); return PoseObject.Parse(pose); } @@ -316,7 +334,8 @@ public async Task GetPose() public async Task GetHardwareStatus() { await SendCommandAsync("GET_HARDWARE_STATUS"); - var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS"); + var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS", + @"(, *([^,\[\]()]+|\[[^\[\]()]*\]|\([^\[\]()]*\))){11}"); return HardwareStatus.Parse(status); } @@ -326,7 +345,7 @@ public async Task GetHardwareStatus() public async Task GetLearningMode() { await SendCommandAsync("GET_LEARNING_MODE"); - var mode = await ReceiveAnswerAsync("GET_LEARNING_MODE"); + var mode = await ReceiveAnswerAsync("GET_LEARNING_MODE", ", *(TRUE|FALSE)"); return bool.Parse(mode); } @@ -336,7 +355,7 @@ public async Task GetLearningMode() public async Task GetDigitalIOState() { await SendCommandAsync("GET_DIGITAL_IO_STATE"); - var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE"); + var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE", @"(, *\[[^]]*\]){8}"); var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); var matches = regex.Matches(state); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Properties/AssemblyInfo.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..98682e85 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleToAttribute("NiryoOneClient.Tests")] From 04ac2d72a3c66d5a9cf7bc86e827a674c4505831 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Tue, 12 Nov 2019 07:49:07 +0100 Subject: [PATCH 24/38] Rename old incorrect test method --- .../NiryoOneConnectionTest.cs | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 8244b2ef..f69dc845 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -54,8 +54,8 @@ public MyTextReader() new Span(value.ToArray()).CopyTo(buffer.Span); return value.Length; } - } #pragma warning restore 1998 + } private MyTextReader _streamReader; private TextWriter _streamWriter; @@ -69,7 +69,7 @@ public NiryoOneConnectionTest() _connection = new NiryoOneConnection(_streamReader, _streamWriter); } - private void SetupReadLineAsyncReturn_(params string[] values) + private void SetupReadAsyncReturn_(params string[] values) { _streamReader.Values = values; } @@ -77,7 +77,7 @@ private void SetupReadLineAsyncReturn_(params string[] values) [TestMethod] public async Task Calibrate_SuccessfulAuto_Works() { - SetupReadLineAsyncReturn_("CALIBRATE:OK"); + SetupReadAsyncReturn_("CALIBRATE:OK"); await _connection.Calibrate(CalibrateMode.AUTO); await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); } @@ -85,7 +85,7 @@ public async Task Calibrate_SuccessfulAuto_Works() [TestMethod] public async Task Calibrate_SuccessfulManual_Works() { - SetupReadLineAsyncReturn_("CALIBRATE:OK\n"); + SetupReadAsyncReturn_("CALIBRATE:OK\n"); await _connection.Calibrate(CalibrateMode.MANUAL); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); } @@ -93,7 +93,7 @@ public async Task Calibrate_SuccessfulManual_Works() [TestMethod] public async Task Calibrate_Failure_Throws() { - SetupReadLineAsyncReturn_("CALIBRATE:KO,\"Sucks to be sucky\""); + SetupReadAsyncReturn_("CALIBRATE:KO,\"Sucks to be sucky\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); @@ -102,7 +102,7 @@ public async Task Calibrate_Failure_Throws() [TestMethod] public async Task SetLearningMode_True_Works() { - SetupReadLineAsyncReturn_("SET_LEARNING_MODE:OK"); + SetupReadAsyncReturn_("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(true); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } @@ -110,7 +110,7 @@ public async Task SetLearningMode_True_Works() [TestMethod] public async Task SetLearningMode_False_Works() { - SetupReadLineAsyncReturn_("SET_LEARNING_MODE:OK"); + SetupReadAsyncReturn_("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(false); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } @@ -118,7 +118,7 @@ public async Task SetLearningMode_False_Works() [TestMethod] public async Task SetLearningMode_WrongResponse_Throws() { - SetupReadLineAsyncReturn_("COCO:OK"); + SetupReadAsyncReturn_("COCO:OK"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("Wrong command response received.", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -127,7 +127,7 @@ public async Task SetLearningMode_WrongResponse_Throws() [TestMethod] public async Task SetLearningMode_ErrorResponse_Throws() { - SetupReadLineAsyncReturn_("SET_LEARNING_MODE:KO,\"No good\""); + SetupReadAsyncReturn_("SET_LEARNING_MODE:KO,\"No good\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("No good", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -136,7 +136,7 @@ public async Task SetLearningMode_ErrorResponse_Throws() [TestMethod] public async Task MoveJoints_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("MOVE_JOINTS:OK"); + SetupReadAsyncReturn_("MOVE_JOINTS:OK"); await _connection.MoveJoints(new RobotJoints(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -146,7 +146,7 @@ await _connection.MoveJoints(new RobotJoints(new[] { [TestMethod] public async Task MovePose_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("MOVE_POSE:OK"); + SetupReadAsyncReturn_("MOVE_POSE:OK"); await _connection.MovePose(new PoseObject(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -156,7 +156,7 @@ await _connection.MovePose(new PoseObject(new[] { [TestMethod] public async Task ShiftPose_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("SHIFT_POSE:OK"); + SetupReadAsyncReturn_("SHIFT_POSE:OK"); await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); } @@ -164,7 +164,7 @@ public async Task ShiftPose_Sample_SendsCorrectly() [TestMethod] public async Task SetArmMaxVelocity_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("SET_ARM_MAX_VELOCITY:OK"); + SetupReadAsyncReturn_("SET_ARM_MAX_VELOCITY:OK"); await _connection.SetArmMaxVelocity(50); await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); } @@ -172,7 +172,7 @@ public async Task SetArmMaxVelocity_Sample_SendsCorrectly() [TestMethod] public async Task EnableJoystick_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("ENABLE_JOYSTICK:OK"); + SetupReadAsyncReturn_("ENABLE_JOYSTICK:OK"); await _connection.EnableJoystick(false); await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); } @@ -180,7 +180,7 @@ public async Task EnableJoystick_Sample_SendsCorrectly() [TestMethod] public async Task SetPinMode_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("SET_PIN_MODE:OK"); + SetupReadAsyncReturn_("SET_PIN_MODE:OK"); await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); } @@ -188,7 +188,7 @@ public async Task SetPinMode_Sample_SendsCorrectly() [TestMethod] public async Task DigitalWrite_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("DIGITAL_WRITE:OK"); + SetupReadAsyncReturn_("DIGITAL_WRITE:OK"); await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); } @@ -196,7 +196,7 @@ public async Task DigitalWrite_Sample_SendsCorrectly() [TestMethod] public async Task DigitalRead_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("DIGITAL_READ:OK,HIGH"); + SetupReadAsyncReturn_("DIGITAL_READ:OK,HIGH"); await _connection.DigitalRead(RobotPin.GPIO_1A); await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); } @@ -204,7 +204,7 @@ public async Task DigitalRead_Sample_SendsCorrectly() [TestMethod] public async Task ChangeTool_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("CHANGE_TOOL:OK"); + SetupReadAsyncReturn_("CHANGE_TOOL:OK"); await _connection.ChangeTool(RobotTool.GRIPPER_2); await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); } @@ -212,7 +212,7 @@ public async Task ChangeTool_Sample_SendsCorrectly() [TestMethod] public async Task OpenGripper_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("OPEN_GRIPPER:OK"); + SetupReadAsyncReturn_("OPEN_GRIPPER:OK"); await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); } @@ -220,7 +220,7 @@ public async Task OpenGripper_Sample_SendsCorrectly() [TestMethod] public async Task CloseGripper_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("CLOSE_GRIPPER:OK"); + SetupReadAsyncReturn_("CLOSE_GRIPPER:OK"); await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); } @@ -228,7 +228,7 @@ public async Task CloseGripper_Sample_SendsCorrectly() [TestMethod] public async Task PullAirVacuumPump_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("PULL_AIR_VACUUM_PUMP:OK"); + SetupReadAsyncReturn_("PULL_AIR_VACUUM_PUMP:OK"); await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -236,7 +236,7 @@ public async Task PullAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task PushAirVacuumPump_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("PUSH_AIR_VACUUM_PUMP:OK"); + SetupReadAsyncReturn_("PUSH_AIR_VACUUM_PUMP:OK"); await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -244,7 +244,7 @@ public async Task PushAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task SetupElectromagnet_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("SETUP_ELECTROMAGNET:OK"); + SetupReadAsyncReturn_("SETUP_ELECTROMAGNET:OK"); await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -252,7 +252,7 @@ public async Task SetupElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task ActivateElectromagnet_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("ACTIVATE_ELECTROMAGNET:OK"); + SetupReadAsyncReturn_("ACTIVATE_ELECTROMAGNET:OK"); await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -260,7 +260,7 @@ public async Task ActivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task DeactivateElectromagnet_Sample_SendsCorrectly() { - SetupReadLineAsyncReturn_("DEACTIVATE_ELECTROMAGNET:OK"); + SetupReadAsyncReturn_("DEACTIVATE_ELECTROMAGNET:OK"); await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); } @@ -268,7 +268,7 @@ public async Task DeactivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task GetJoints_Sample_Works() { - SetupReadLineAsyncReturn_("GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0"); + SetupReadAsyncReturn_("GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0"); var joints = await _connection.GetJoints(); CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); } @@ -276,7 +276,7 @@ public async Task GetJoints_Sample_Works() [TestMethod] public async Task GetPose_Sample_Works() { - SetupReadLineAsyncReturn_("GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12"); + SetupReadAsyncReturn_("GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12"); var pose = await _connection.GetPose(); CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, pose.ToArray()); @@ -285,7 +285,7 @@ public async Task GetPose_Sample_Works() [TestMethod] public async Task GetHardwareStatus_Sample_Works() { - SetupReadLineAsyncReturn_("GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)"); + SetupReadAsyncReturn_("GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)"); var status = await _connection.GetHardwareStatus(); Assert.AreEqual(59, status.RpiTemperature); Assert.AreEqual(2, status.HardwareVersion); @@ -303,7 +303,7 @@ public async Task GetHardwareStatus_Sample_Works() [TestMethod] public async Task GetLearningMode_Sample_Works() { - SetupReadLineAsyncReturn_("GET_LEARNING_MODE:OK,FALSE"); + SetupReadAsyncReturn_("GET_LEARNING_MODE:OK,FALSE"); var mode = await _connection.GetLearningMode(); await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); Assert.AreEqual(false, mode); @@ -312,7 +312,7 @@ public async Task GetLearningMode_Sample_Works() [TestMethod] public async Task GetDigitalIOState_Sample_Works() { - SetupReadLineAsyncReturn_("GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]"); + SetupReadAsyncReturn_("GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]"); var state = await _connection.GetDigitalIOState(); Assert.AreEqual(2, state[0].PinId); Assert.AreEqual("1A", state[0].Name); @@ -357,7 +357,7 @@ public async Task GetDigitalIOState_Sample_Works() [TestMethod] public async Task ReceiveAnswerAsync_GroupedInOneRead_Works() { - SetupReadLineAsyncReturn_("HEJ:OKBLA:OKMERP:OK"); + SetupReadAsyncReturn_("HEJ:OKBLA:OKMERP:OK"); var a1 = await _connection.ReceiveAnswerAsync("HEJ"); Assert.AreEqual("", a1); var a2 = await _connection.ReceiveAnswerAsync("BLA"); @@ -369,7 +369,7 @@ public async Task ReceiveAnswerAsync_GroupedInOneRead_Works() { [TestMethod] public async Task ReceiveAnswerAsync_SplitInMultipleReads_Works() { - SetupReadLineAsyncReturn_("HEJ:O", "KBLA:", "OKMERP", ":OK"); + SetupReadAsyncReturn_("HEJ:O", "KBLA:", "OKMERP", ":OK"); var a1 = await _connection.ReceiveAnswerAsync("HEJ"); Assert.AreEqual("", a1); var a2 = await _connection.ReceiveAnswerAsync("BLA"); @@ -381,7 +381,7 @@ public async Task ReceiveAnswerAsync_SplitInMultipleReads_Works() [TestMethod] public async Task ReceiveAnswerAsync_ArbitraryWhitespaceBetween_Works() { - SetupReadLineAsyncReturn_("HEJ:OK\n\t BLA:OK \t\r\nMERP:OK\r\n"); + SetupReadAsyncReturn_("HEJ:OK\n\t BLA:OK \t\r\nMERP:OK\r\n"); var a1 = await _connection.ReceiveAnswerAsync("HEJ"); Assert.AreEqual("", a1); var a2 = await _connection.ReceiveAnswerAsync("BLA"); From 1b3f0f54e5a4ec36907118d5981dc73970ab922b Mon Sep 17 00:00:00 2001 From: Mikael Bertze Date: Wed, 20 Nov 2019 15:52:20 +0100 Subject: [PATCH 25/38] Adds interface to NiryoOneClient and NiryoOneConnection --- .../HardwareStatusTest.cs | 52 ------ .../csharp/NiryoOneClient/CalibrateMode.cs | 42 +++++ .../csharp/NiryoOneClient/HardwareStatus.cs | 67 ------- .../csharp/NiryoOneClient/INiryoOneClient.cs | 44 +++++ .../NiryoOneClient/INiryoOneConnection.cs | 167 ++++++++++++++++++ .../csharp/NiryoOneClient/NiryoOneClient.cs | 23 +-- .../NiryoOneClient/NiryoOneConnection.cs | 35 ++-- .../csharp/NiryoOneClient/ParserUtils.cs | 147 +++++++++++++++ .../csharp/NiryoOneClient/PoseObject.cs | 10 -- .../csharp/NiryoOneClient/RobotJoints.cs | 11 -- .../clients/csharp/NiryoOneClient/RobotPin.cs | 22 --- 11 files changed, 420 insertions(+), 200 deletions(-) delete mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/CalibrateMode.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneConnection.cs create mode 100644 niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs deleted file mode 100644 index 8ba82ded..00000000 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/HardwareStatusTest.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* MIT License - - Copyright (c) 2019 Niryo - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace NiryoOneClient.Tests -{ - [TestClass] - public class HardwareStatusTest - { - [TestMethod] - public void Parse_Sample_Works() - { - // Arrange - var s = "[59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)]"; - // Act - var hs = HardwareStatus.Parse(s); - // Assert - Assert.AreEqual(59, hs.RpiTemperature); - Assert.AreEqual(2, hs.HardwareVersion); - Assert.AreEqual(true, hs.ConnectionUp); - Assert.AreEqual("", hs.ErrorMessage); - Assert.AreEqual(0, hs.CalibrationNeeded); - Assert.AreEqual(false, hs.CalibrationInProgress); - CollectionAssert.AreEqual(new[] { "Stepper Axis 1", "Stepper Axis 2", "Stepper Axis 3", "Servo Axis 4", "Servo Axis 5", "Servo Axis 6" }, hs.MotorNames); - CollectionAssert.AreEqual(new[] { "Niryo Stepper", "Niryo Stepper", "Niryo Stepper", "DXL XL-430", "DXL XL-430", "DXL XL-320" }, hs.MotorTypes); - CollectionAssert.AreEqual(new[] { 34, 34, 37, 43, 45, 37 }, hs.Temperatures); - CollectionAssert.AreEqual(new[] { 0.0m, 0.0m, 0.0m, 11.3m, 11.2m, 7.9m }, hs.Voltages); - CollectionAssert.AreEqual(new[] { 0, 0, 0, 0, 0, 0 }, hs.HardwareErrors); - } - } -} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/CalibrateMode.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/CalibrateMode.cs new file mode 100644 index 00000000..91c4f3db --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/CalibrateMode.cs @@ -0,0 +1,42 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +namespace NiryoOneClient +{ + /// + /// The type of calibration to be performed + /// + public enum CalibrateMode + { + /// + /// Automatic calibration where the robot performs a set of move to establish calibration + /// + AUTO, + + /// + /// Manual calibration where the robot is manually positioned in the calibration pose and that + /// pose is used to establish calibration + /// + MANUAL + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs index 8b194e63..34d23717 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/HardwareStatus.cs @@ -32,73 +32,6 @@ namespace NiryoOneClient /// public class HardwareStatus { - private static string Strip_(string s, char prefix, char suffix) - { - if (!s.StartsWith(prefix)) - throw new ArgumentException(); - if (!s.EndsWith(suffix)) - throw new ArgumentException(); - return s.Substring(1, s.Length - 2); - } - - private static string[] ParseStrings_(string s) - { - var regex = new System.Text.RegularExpressions.Regex(@"'[^']*'"); - return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); - } - - private static T[] ParseNumbers_(string s, Func parser) - { - var regex = new System.Text.RegularExpressions.Regex(@"[0-9]+(\.[0-9]*)?"); - return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); - } - - /// - /// Parse a string representation of the hardware status in the format of the tcp server - /// - /// The string representation - /// A parsed object - public static HardwareStatus Parse(string data) - { - var regex = new System.Text.RegularExpressions.Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); - var matches = regex.Matches(data); - - if (matches.Count != 11) - { - throw new NiryoOneException("Incorrect answer received, cannot understand received format."); - } - - var rpiTemperature = int.Parse(matches[0].Value); - var hardwareVersion = int.Parse(matches[1].Value); - var connectionUp = bool.Parse(matches[2].Value); - var errorMessage = Strip_(matches[3].Value, '\'', '\''); - var calibrationNeeded = int.Parse(matches[4].Value); - var calibrationInProgress = bool.Parse(matches[5].Value); - - var motorNames = ParseStrings_(matches[6].Value); - var motorTypes = ParseStrings_(matches[7].Value); - - var temperatures = ParseNumbers_(matches[8].Value, int.Parse); - var voltages = ParseNumbers_(matches[9].Value, x => decimal.Parse(x, CultureInfo.InvariantCulture)); - var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); - - var hardwareStatus = new HardwareStatus() - { - RpiTemperature = rpiTemperature, - HardwareVersion = hardwareVersion, - ConnectionUp = connectionUp, - ErrorMessage = errorMessage, - CalibrationNeeded = calibrationNeeded, - CalibrationInProgress = calibrationInProgress, - MotorNames = motorNames, - MotorTypes = motorTypes, - Temperatures = temperatures, - Voltages = voltages, - HardwareErrors = hardwareErrors - }; - return hardwareStatus; - } - /// /// The core temperature of the Raspberry Pi /// diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs new file mode 100644 index 00000000..67a84400 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs @@ -0,0 +1,44 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System.Threading.Tasks; + +namespace NiryoOneClient +{ + /// + /// A client capable of connecting to the tcp server of a Niryo One robotic arm + /// + public interface INiryoOneClient + { + /// + /// Create a connection to the robot + /// + /// A NiryoOneConnection object used for sending commands to the robot + Task Connect(); + + /// + /// Dispose the object + /// + void Dispose(); + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneConnection.cs new file mode 100644 index 00000000..0a4a276b --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneConnection.cs @@ -0,0 +1,167 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System.Threading.Tasks; + +namespace NiryoOneClient +{ + /// + /// A connection object allowing sending commands to the tcp server on a Niryo One robotic arm + /// + public interface INiryoOneConnection + { + /// + /// Request calibration. + /// Whether to request automatic or manual calibration + /// + Task Calibrate(CalibrateMode mode); + + /// + /// Set whether the robot should be in learning mode or not. + /// Activate learning mode or not + /// + Task SetLearningMode(bool mode); + + /// + /// Move joints to specified configuration. + /// The desired destination joint configuration + /// + Task MoveJoints(RobotJoints joints); + + /// + /// Move joints to specified pose. + /// The desired destination pose + /// + Task MovePose(PoseObject pose); + + /// + /// Shift the pose along one axis. + /// Which axis to shift + /// The amount to shift (meters or radians) + /// + Task ShiftPose(RobotAxis axis, float value); + + /// + /// Set the maximum arm velocity.false + /// The maximum velocity in percent of maximum velocity. + /// + Task SetArmMaxVelocity(int velocity); + + /// + /// Enable or disable joystick control.false + /// + Task EnableJoystick(bool mode); + + /// + /// Configure a GPIO pin for input or output. + /// + Task SetPinMode(RobotPin pin, PinMode mode); + + /// + /// Write to a digital pin configured as output. + /// + Task DigitalWrite(RobotPin pin, DigitalState state); + + /// + /// Read from a digital pin configured as input. + /// + Task DigitalRead(RobotPin pin); + + /// + /// Select which tool is connected to the robot. + /// + Task ChangeTool(RobotTool tool); + + /// + /// Open the gripper. + /// Which gripper to open + /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. + /// + Task OpenGripper(RobotTool gripper, int speed); + + /// + /// Close the gripper. + /// Which gripper to close + /// The speed to use. Must be between 0 and 1000, recommended values between 100 and 500. + /// + Task CloseGripper(RobotTool gripper, int speed); + + /// + /// Pull air using the vacuum pump. + /// Must be VACUUM_PUMP_1. Only one type available for now. + /// + Task PullAirVacuumPump(RobotTool vacuumPump); + + /// + /// Push air using the vacuum pump. + /// Must be VACUUM_PUMP_1. Only one type available for now. + /// + Task PushAirVacuumPump(RobotTool vacuumPump); + + /// + /// Setup the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + Task SetupElectromagnet(RobotTool tool, RobotPin pin); + + /// + /// Activate the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + Task ActivateElectromagnet(RobotTool tool, RobotPin pin); + + /// + /// Deactivate the electromagnet. + /// Must be ELECTROMAGNET_1. Only one type available for now. + /// The pin to which the magnet is connected. + /// + Task DeactivateElectromagnet(RobotTool tool, RobotPin pin); + + /// + /// Get the current joint configuration. + /// + Task GetJoints(); + + /// + /// Get the current pose. + /// + Task GetPose(); + + /// + /// Get the current hardware status. + /// + Task GetHardwareStatus(); + + /// + /// Get whether the robot is in learning mode. + /// + Task GetLearningMode(); + + /// + /// Get the current state of the digital io pins. + /// + Task GetDigitalIOState(); + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index abc2e8ba..5ccc0d89 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -27,31 +27,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace NiryoOneClient { - /// - /// The type of calibration to be performed - /// - public enum CalibrateMode - { - /// - /// Automatic calibration where the robot performs a set of move to establish calibration - /// - AUTO, - - /// - /// Manual calibration where the robot is manually positioned in the calibration pose and that - /// pose is used to establish calibration - /// - MANUAL - } - /// /// A client capable of connecting to the tcp server of a Niryo One robotic arm /// - public class NiryoOneClient : IDisposable + public class NiryoOneClient : IDisposable, INiryoOneClient { private TcpClient _client; - private int _port; - private string _server; + private readonly int _port; + private readonly string _server; private NetworkStream _stream; private NiryoOneConnection _connection; diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index aa512064..145a47d9 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -1,4 +1,3 @@ - /* MIT License Copyright (c) 2019 Niryo @@ -35,7 +34,7 @@ namespace NiryoOneClient /// /// A connection object allowing sending commands to the tcp server on a Niryo One robotic arm /// - public class NiryoOneConnection + public class NiryoOneConnection : INiryoOneConnection { private readonly TextWriter _textWriter; private readonly TextReader _textReader; @@ -67,8 +66,8 @@ internal async Task WriteLineAsync(string s) /// The string read internal async Task ReadAsync() { - const int blocksize = 512; - Memory memory = new Memory(new char[blocksize]); + const int blockSize = 512; + Memory memory = new Memory(new char[blockSize]); var count = await _textReader.ReadAsync(memory); return new string(memory.Span.Slice(0, count).ToArray()); } @@ -76,28 +75,28 @@ internal async Task ReadAsync() /// /// Send a command to the tcp server /// - /// The type of command + /// The type of command /// The arguments - protected async Task SendCommandAsync(string command_type, params string[] args) + protected async Task SendCommandAsync(string commandType, params string[] args) { string cmd; if (args.Any()) - cmd = $"{command_type}:{string.Join(",", args)}"; + cmd = $"{commandType}:{string.Join(",", args)}"; else - cmd = command_type; + cmd = commandType; await WriteLineAsync(cmd); } private string _stringBuf = ""; /// - /// Receive an answer fromt the tcp server related to a previously sent command + /// Receive an answer from the tcp server related to a previously sent command /// - /// The command for which a response is expected + /// The command for which a response is expected /// Optionally, the regular expression that the successful response arguments /// are supposed to match /// The data portion of the response - internal async Task ReceiveAnswerAsync(string command_type, string regex="") + internal async Task ReceiveAnswerAsync(string commandType, string regex = "") { var fullRegex = new Regex($"^[A-Z_]+:(OK{regex}|KO,\"[^\"]*\")"); string s = _stringBuf; @@ -110,10 +109,10 @@ internal async Task ReceiveAnswerAsync(string command_type, string regex var match = fullRegex.Match(s); var result = match.Value.Trim(); _stringBuf = s.Substring(match.Index + match.Length).TrimStart(); - + var colonSplit = result.Split(':', 2); var cmd = colonSplit[0]; - if (cmd != command_type) + if (cmd != commandType) throw new NiryoOneException("Wrong command response received."); var commaSplit2 = colonSplit[1].Split(',', 2); var status = commaSplit2[0]; @@ -315,7 +314,7 @@ public async Task GetJoints() { await SendCommandAsync("GET_JOINTS"); var joints = await ReceiveAnswerAsync("GET_JOINTS", "(, *[-0-9.e]+){6}"); - return RobotJoints.Parse(joints); + return ParserUtils.ParseRobotJoints(joints); } /// @@ -325,7 +324,7 @@ public async Task GetPose() { await SendCommandAsync("GET_POSE"); var pose = await ReceiveAnswerAsync("GET_POSE", "(, *[-0-9.e]+){6}"); - return PoseObject.Parse(pose); + return ParserUtils.ParsePoseObject(pose); } /// @@ -334,9 +333,9 @@ public async Task GetPose() public async Task GetHardwareStatus() { await SendCommandAsync("GET_HARDWARE_STATUS"); - var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS", + var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS", @"(, *([^,\[\]()]+|\[[^\[\]()]*\]|\([^\[\]()]*\))){11}"); - return HardwareStatus.Parse(status); + return ParserUtils.ParseHardwareStatus(status); } /// @@ -360,7 +359,7 @@ public async Task GetDigitalIOState() var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); var matches = regex.Matches(state); - return matches.Select(m => DigitalPinObject.Parse(m.Value)).ToArray(); + return matches.Select(m => ParserUtils.ParseDigitalPinObject(m.Value)).ToArray(); } } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs new file mode 100644 index 00000000..17523565 --- /dev/null +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs @@ -0,0 +1,147 @@ +/* MIT License + + Copyright (c) 2019 Niryo + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +using System; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +namespace NiryoOneClient +{ + /// + /// A helper class for parsing NiryoOne types in the format used by the tcp server. + /// + public static class ParserUtils + { + /// + /// Parse a string representation of the hardware status in the format of the tcp server + /// + /// The string representation + /// A parsed object + public static HardwareStatus ParseHardwareStatus(string data) + { + var regex = new Regex(@"((?:\[[^[\]]+\])|(?:\([^\)]+\))|True|False|\d+|'\w*')"); + var matches = regex.Matches(data); + + if (matches.Count != 11) + { + throw new NiryoOneException("Incorrect answer received, cannot understand received format."); + } + + var rpiTemperature = int.Parse(matches[0].Value); + var hardwareVersion = int.Parse(matches[1].Value); + var connectionUp = bool.Parse(matches[2].Value); + var errorMessage = Strip_(matches[3].Value, '\'', '\''); + var calibrationNeeded = int.Parse(matches[4].Value); + var calibrationInProgress = bool.Parse(matches[5].Value); + + var motorNames = ParseStrings_(matches[6].Value); + var motorTypes = ParseStrings_(matches[7].Value); + + var temperatures = ParseNumbers_(matches[8].Value, int.Parse); + var voltages = ParseNumbers_(matches[9].Value, x => decimal.Parse(x, CultureInfo.InvariantCulture)); + var hardwareErrors = ParseNumbers_(matches[10].Value, int.Parse); + + var hardwareStatus = new HardwareStatus() + { + RpiTemperature = rpiTemperature, + HardwareVersion = hardwareVersion, + ConnectionUp = connectionUp, + ErrorMessage = errorMessage, + CalibrationNeeded = calibrationNeeded, + CalibrationInProgress = calibrationInProgress, + MotorNames = motorNames, + MotorTypes = motorTypes, + Temperatures = temperatures, + Voltages = voltages, + HardwareErrors = hardwareErrors + }; + return hardwareStatus; + } + + + /// + /// Parse a string representation of a pose in the format of the tcp server + /// + /// The string representation + /// A parsed object + public static PoseObject ParsePoseObject(string s) + { + return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); + } + + + /// + /// Parse a string representation of a joint configuration in the format of the tcp server + /// + /// The string representation + /// A parsed object + public static RobotJoints ParseRobotJoints(string s) + { + return new RobotJoints(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); + } + + + /// + /// Parse a string representation of a digital pin state in the format of the tcp server + /// + /// The string representation + /// A parsed object + public static DigitalPinObject ParseDigitalPinObject(string s) + { + if (!s.StartsWith('[') || !s.EndsWith(']')) + throw new ArgumentException(); + + var ss = s.Substring(1, s.Length - 2).Split(", "); + + return new DigitalPinObject + { + PinId = int.Parse(ss[0]), + Name = ss[1].Trim().Substring(1, ss[1].Length - 2), + Mode = (PinMode)int.Parse(ss[2]), + State = (DigitalState)int.Parse(ss[3]) + }; + } + + private static string Strip_(string s, char prefix, char suffix) + { + if (!s.StartsWith(prefix)) + throw new ArgumentException(); + if (!s.EndsWith(suffix)) + throw new ArgumentException(); + return s.Substring(1, s.Length - 2); + } + + private static string[] ParseStrings_(string s) + { + var regex = new Regex(@"'[^']*'"); + return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); + } + + private static T[] ParseNumbers_(string s, Func parser) + { + var regex = new Regex(@"[0-9]+(\.[0-9]*)?"); + return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); + } + } +} \ No newline at end of file diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs index 5e67c9ab..7f39fcec 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/PoseObject.cs @@ -65,16 +65,6 @@ public PoseObject(float[] j) _j = j; } - /// - /// Parse a string representation of a pose in the format of the tcp server - /// - /// The string representation - /// A parsed object - public static PoseObject Parse(string s) - { - return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); - } - /// /// The X position in meters. /// diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs index 20159d86..e0d6b99b 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotJoints.cs @@ -62,17 +62,6 @@ public RobotJoints(float[] j) _j = j; } - - /// - /// Parse a string representation of a joint configuration in the format of the tcp server - /// - /// The string representation - /// A parsed object - public static RobotJoints Parse(string s) - { - return new RobotJoints(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); - } - /// The value of the first joint, in radians public float J1 { get => _j[0]; set => _j[0] = value; } /// The value of the second joint, in radians diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs index af235f0f..bcfac1a2 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/RobotPin.cs @@ -95,27 +95,5 @@ public class DigitalPinObject /// Whether the pin is low or high /// public DigitalState State; - - - /// - /// Parse a string representation of a digital pin state in the format of the tcp server - /// - /// The string representation - /// A parsed object - public static DigitalPinObject Parse(string s) - { - if (!s.StartsWith('[') || !s.EndsWith(']')) - throw new ArgumentException(); - - var ss = s.Substring(1, s.Length - 2).Split(", "); - - return new DigitalPinObject - { - PinId = int.Parse(ss[0]), - Name = ss[1].Trim().Substring(1, ss[1].Length - 2), - Mode = (PinMode)int.Parse(ss[2]), - State = (DigitalState)int.Parse(ss[3]) - }; - } } } \ No newline at end of file From 15b207f2576674b581980a6328c10993c0437a54 Mon Sep 17 00:00:00 2001 From: Mikael Bertze Date: Wed, 20 Nov 2019 15:57:00 +0100 Subject: [PATCH 26/38] Return interface in Connect method --- .../clients/csharp/NiryoOneClient/INiryoOneClient.cs | 2 +- .../clients/csharp/NiryoOneClient/NiryoOneClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs index 67a84400..3f49c0f6 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs @@ -34,7 +34,7 @@ public interface INiryoOneClient /// Create a connection to the robot /// /// A NiryoOneConnection object used for sending commands to the robot - Task Connect(); + Task Connect(); /// /// Dispose the object diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index 5ccc0d89..6fb2ac25 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -53,7 +53,7 @@ public NiryoOneClient(string server, int port = 40001) /// Create a connection to the robot /// /// A NiryoOneConnection object used for sending commands to the robot - public async Task Connect() + public async Task Connect() { if (_client != null) { From 24d1b785a85cfb38577f475d828554a953d9fd04 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 20 Nov 2019 17:23:02 +0100 Subject: [PATCH 27/38] Remove Dispose() from INiryoOneClient --- .../clients/csharp/NiryoOneClient/INiryoOneClient.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs index 3f49c0f6..831c8ccf 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/INiryoOneClient.cs @@ -35,10 +35,5 @@ public interface INiryoOneClient /// /// A NiryoOneConnection object used for sending commands to the robot Task Connect(); - - /// - /// Dispose the object - /// - void Dispose(); } } \ No newline at end of file From 18818e439ec2f642024786e5f88c4e55415eb87a Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 20 Nov 2019 19:30:03 +0100 Subject: [PATCH 28/38] Don't create nuget packages for the example code --- niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj index 20006a0a..48ee88a9 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj +++ b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj @@ -1,12 +1,13 @@ - - + + Exe netcoreapp3.0 + false From 30b0fe4956d3f0e4ed89db36c5bf679307d6fc1b Mon Sep 17 00:00:00 2001 From: Mikael Bertze Date: Thu, 21 Nov 2019 08:20:19 +0100 Subject: [PATCH 29/38] Fixing regexp for KO response --- .../clients/csharp/NiryoOneClient/NiryoOneConnection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 145a47d9..82225ffc 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -98,7 +98,8 @@ protected async Task SendCommandAsync(string commandType, params string[] args) /// The data portion of the response internal async Task ReceiveAnswerAsync(string commandType, string regex = "") { - var fullRegex = new Regex($"^[A-Z_]+:(OK{regex}|KO,\"[^\"]*\")"); + + var fullRegex = new Regex($"^[A-Z_]+:(OK{regex}|KO,.*)"); string s = _stringBuf; var sb = new StringBuilder(s); while (!fullRegex.IsMatch(s)) From d9b12d5bed14cc0fd9c4aa708692675ba6641dd2 Mon Sep 17 00:00:00 2001 From: Mikael Bertze Date: Thu, 21 Nov 2019 08:26:31 +0100 Subject: [PATCH 30/38] fixing exception message --- .../clients/csharp/NiryoOneClient/NiryoOneConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 82225ffc..38be4af7 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -118,7 +118,7 @@ internal async Task ReceiveAnswerAsync(string commandType, string regex var commaSplit2 = colonSplit[1].Split(',', 2); var status = commaSplit2[0]; if (status != "OK") - throw new NiryoOneException(commaSplit2[1].TrimStart('"').TrimEnd('"')); + throw new NiryoOneException(commaSplit2[1]); if (commaSplit2.Length > 1) return commaSplit2[1]; From 24fd05033f400803c1de252828b4f2f32a5b5912 Mon Sep 17 00:00:00 2001 From: Mikael Bertze Date: Thu, 21 Nov 2019 08:30:46 +0100 Subject: [PATCH 31/38] Fixing KO response tests --- .../csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index f69dc845..2aa6fcb6 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -93,7 +93,7 @@ public async Task Calibrate_SuccessfulManual_Works() [TestMethod] public async Task Calibrate_Failure_Throws() { - SetupReadAsyncReturn_("CALIBRATE:KO,\"Sucks to be sucky\""); + SetupReadAsyncReturn_("CALIBRATE:KO,Sucks to be sucky"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); @@ -127,7 +127,7 @@ public async Task SetLearningMode_WrongResponse_Throws() [TestMethod] public async Task SetLearningMode_ErrorResponse_Throws() { - SetupReadAsyncReturn_("SET_LEARNING_MODE:KO,\"No good\""); + SetupReadAsyncReturn_("SET_LEARNING_MODE:KO,No good"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("No good", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); From 192287b9c748c00aafb7487d0e8d8b70ae6f8f8b Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Fri, 22 Nov 2019 08:45:03 +0100 Subject: [PATCH 32/38] Make the TCP protocol be line-based --- .../python/niryo_one_tcp_client/tcp_client.py | 11 +++++++---- .../src/niryo_one_tcp_server/command_interpreter.py | 6 +++--- .../src/niryo_one_tcp_server/tcp_server.py | 13 ++++++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/niryo_one_tcp_server/clients/python/niryo_one_tcp_client/tcp_client.py b/niryo_one_tcp_server/clients/python/niryo_one_tcp_client/tcp_client.py index df47bdf3..0c799f3a 100644 --- a/niryo_one_tcp_server/clients/python/niryo_one_tcp_client/tcp_client.py +++ b/niryo_one_tcp_server/clients/python/niryo_one_tcp_client/tcp_client.py @@ -49,6 +49,7 @@ def __init__(self, timeout=5): self.__is_connected = False self.__timeout = timeout self.__client_socket = None + self.__client_sockfile = None self.__packet_builder = PacketBuilder() def __del__(self): @@ -58,12 +59,14 @@ def quit(self): self.__is_running = False self.__shutdown_connection() self.__client_socket = None + self.__client_sockfile = None def __shutdown_connection(self): if self.__client_socket is not None and self.__is_connected is True: try: self.__client_socket.shutdown(socket.SHUT_RDWR) self.__client_socket.close() + self.__client_sockfile.close() except socket.error as e: pass self.__is_connected = False @@ -85,6 +88,7 @@ def connect(self, ip_address): print("Connected to server ({}) on port: {}".format(ip_address, self.__port)) self.__is_connected = True self.__client_socket.settimeout(None) + self.__client_sockfile = self.__client_socket.makefile() return self.__is_connected @@ -244,19 +248,18 @@ def send_command(self, command_type, parameter_list=None): if self.__is_connected is False: raise self.ClientNotConnectedException() send_success = False - if self.__client_socket is not None: + if self.__client_sockfile is not None: try: packet = self.__packet_builder.build_command_packet(command_type, parameter_list) - self.__client_socket.send(packet) + self.__client_sockfile.write(packet + "\n") except socket.error as e: print(e) raise self.HostNotReachableException() return send_success def receive_answer(self): - READ_SIZE = 512 try: - received = self.__client_socket.recv(READ_SIZE) + received = self.__client_sockfile.readline().rstrip() except socket.error as e: print(e) raise self.HostNotReachableException() diff --git a/niryo_one_tcp_server/src/niryo_one_tcp_server/command_interpreter.py b/niryo_one_tcp_server/src/niryo_one_tcp_server/command_interpreter.py index 8f1b192d..6ca2decc 100644 --- a/niryo_one_tcp_server/src/niryo_one_tcp_server/command_interpreter.py +++ b/niryo_one_tcp_server/src/niryo_one_tcp_server/command_interpreter.py @@ -106,7 +106,7 @@ def interpret_command(self, command_received): split_list = command_received.split(":") if len(split_list) > 2: rospy.logwarn("Incorrect command format: " + command_received) - return command_received + ":KO,Incorrect command format." + return command_received + ":KO,\"Incorrect command format.\"" command_string_part = split_list[0].rstrip('\r\n') ret_string = command_string_part + ":" if command_string_part in self.__commands_dict: @@ -121,7 +121,7 @@ def interpret_command(self, command_received): return ret_string + ret except TypeError as e: rospy.logwarn("Incorrect number of parameter(s) given. " + str(e)) - return ret_string + "KO,\"" + "Incorrect number of parameter(s) given.\"" + return ret_string + "KO,\"Incorrect number of parameter(s) given.\"" except python_api.NiryoOneException as e: rospy.logwarn(e) return ret_string + "KO,\"" + str(e) + "\"" @@ -129,7 +129,7 @@ def interpret_command(self, command_received): rospy.logwarn("Incorrect parameter(s) given to : " + command_string_part + " function. " + str(e)) return ret_string + "KO,\"" + str(e) + "\"" else: - return command_string_part + ": " + "KO,\"" + "Unknown command\"" + return command_string_part + ": " + "KO,\"Unknown command\"" def __successfull_answer(self, param_list=None): string_answer = "OK" diff --git a/niryo_one_tcp_server/src/niryo_one_tcp_server/tcp_server.py b/niryo_one_tcp_server/src/niryo_one_tcp_server/tcp_server.py index ebfb456b..8cc0d8dd 100644 --- a/niryo_one_tcp_server/src/niryo_one_tcp_server/tcp_server.py +++ b/niryo_one_tcp_server/src/niryo_one_tcp_server/tcp_server.py @@ -38,6 +38,7 @@ def __init__(self): self.__is_running = True self.__is_busy = False self.__client = None + self.__sockfile = None self.__interpreter = CommandInterpreter() self.__queue = Queue.Queue(1) @@ -78,7 +79,7 @@ def __answer_client_robot_busy(self, command_received): command_name = command_received else: command_name = command_received_split[0] - self.__send(command_name + ":KO,Robot is busy right now, command ignored.") + self.__send(command_name + ":KO,\"Robot is busy right now, command ignored.\"") def __client_socket_event(self, inputs): command_received = self.__read_command() @@ -112,16 +113,17 @@ def __shutdown_client(self): except socket.error as e: pass self.__client.close() + self.__sockfile.close() def __accept_client(self): self.__client, address = self.__server.accept() + self.__sockfile = self.__client.makefile() self.__is_client_connected = True rospy.loginfo("Client connected from ip address: " + str(address)) def __read_command(self): - READ_SIZE = 512 try: - received = self.__client.recv(READ_SIZE) + received = self.__sockfile.readline() except socket.error as e: rospy.logwarn("Error while receiving answer: " + str(e)) return None @@ -130,8 +132,9 @@ def __read_command(self): return received def __send(self, content): - if self.__client is not None: + if self.__sockfile is not None: try: - self.__client.send(content) + self.__sockfile.write(content + "\n") + self.__sockfile.flush() except socket.error as e: rospy.logwarn("Error while sending answer to client: " + str(e)) From 6e064e7341d3a0de6ce945a2a15dba2c75a76a91 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Tue, 3 Dec 2019 20:54:30 +0100 Subject: [PATCH 33/38] Update csharp client to rely on newlines in responses --- .../NiryoOneConnectionTest.cs | 116 +++++------------- .../NiryoOneClient/NiryoOneConnection.cs | 48 +++----- 2 files changed, 48 insertions(+), 116 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 2aa6fcb6..42e49a71 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -21,11 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -using System; using System.IO; using System.Linq; -using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; @@ -35,49 +32,21 @@ namespace NiryoOneClient.Tests [TestClass] public class NiryoOneConnectionTest { - private class MyTextReader : TextReader { - public string[] Values = null; - private int _i; - - public MyTextReader() - { - _i = 0; - } - -#pragma warning disable 1998 - public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) - { - if (_i >= Values.Length) - throw new SocketException(); - var value = Values[_i]; - _i++; - new Span(value.ToArray()).CopyTo(buffer.Span); - return value.Length; - } -#pragma warning restore 1998 - } - - private MyTextReader _streamReader; + private TextReader _streamReader; private TextWriter _streamWriter; - private NiryoOneConnection _connection; public NiryoOneConnectionTest() { - _streamReader = new MyTextReader(); + _streamReader = Substitute.For(); _streamWriter = Substitute.For(); _connection = new NiryoOneConnection(_streamReader, _streamWriter); } - private void SetupReadAsyncReturn_(params string[] values) - { - _streamReader.Values = values; - } - [TestMethod] public async Task Calibrate_SuccessfulAuto_Works() { - SetupReadAsyncReturn_("CALIBRATE:OK"); + _streamReader.ReadLineAsync().Returns("CALIBRATE:OK"); await _connection.Calibrate(CalibrateMode.AUTO); await _streamWriter.Received().WriteLineAsync("CALIBRATE:AUTO"); } @@ -85,7 +54,7 @@ public async Task Calibrate_SuccessfulAuto_Works() [TestMethod] public async Task Calibrate_SuccessfulManual_Works() { - SetupReadAsyncReturn_("CALIBRATE:OK\n"); + _streamReader.ReadLineAsync().Returns("CALIBRATE:OK\n"); await _connection.Calibrate(CalibrateMode.MANUAL); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); } @@ -93,7 +62,7 @@ public async Task Calibrate_SuccessfulManual_Works() [TestMethod] public async Task Calibrate_Failure_Throws() { - SetupReadAsyncReturn_("CALIBRATE:KO,Sucks to be sucky"); + _streamReader.ReadLineAsync().Returns("CALIBRATE:KO,\"Sucks to be sucky\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.Calibrate(CalibrateMode.MANUAL)); Assert.AreEqual("Sucks to be sucky", e.Reason); await _streamWriter.Received().WriteLineAsync("CALIBRATE:MANUAL"); @@ -102,7 +71,7 @@ public async Task Calibrate_Failure_Throws() [TestMethod] public async Task SetLearningMode_True_Works() { - SetupReadAsyncReturn_("SET_LEARNING_MODE:OK"); + _streamReader.ReadLineAsync().Returns("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(true); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:TRUE"); } @@ -110,7 +79,7 @@ public async Task SetLearningMode_True_Works() [TestMethod] public async Task SetLearningMode_False_Works() { - SetupReadAsyncReturn_("SET_LEARNING_MODE:OK"); + _streamReader.ReadLineAsync().Returns("SET_LEARNING_MODE:OK"); await _connection.SetLearningMode(false); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); } @@ -118,7 +87,7 @@ public async Task SetLearningMode_False_Works() [TestMethod] public async Task SetLearningMode_WrongResponse_Throws() { - SetupReadAsyncReturn_("COCO:OK"); + _streamReader.ReadLineAsync().Returns("COCO:OK"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("Wrong command response received.", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -127,7 +96,7 @@ public async Task SetLearningMode_WrongResponse_Throws() [TestMethod] public async Task SetLearningMode_ErrorResponse_Throws() { - SetupReadAsyncReturn_("SET_LEARNING_MODE:KO,No good"); + _streamReader.ReadLineAsync().Returns("SET_LEARNING_MODE:KO,No good"); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("No good", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); @@ -136,7 +105,7 @@ public async Task SetLearningMode_ErrorResponse_Throws() [TestMethod] public async Task MoveJoints_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("MOVE_JOINTS:OK"); + _streamReader.ReadLineAsync().Returns("MOVE_JOINTS:OK"); await _connection.MoveJoints(new RobotJoints(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -146,7 +115,7 @@ await _connection.MoveJoints(new RobotJoints(new[] { [TestMethod] public async Task MovePose_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("MOVE_POSE:OK"); + _streamReader.ReadLineAsync().Returns("MOVE_POSE:OK"); await _connection.MovePose(new PoseObject(new[] { 0.03f, 0.0123f, 0.456f, 0.987f, 0.654f, 0.321f })); @@ -156,7 +125,7 @@ await _connection.MovePose(new PoseObject(new[] { [TestMethod] public async Task ShiftPose_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("SHIFT_POSE:OK"); + _streamReader.ReadLineAsync().Returns("SHIFT_POSE:OK"); await _connection.ShiftPose(RobotAxis.ROLL, 0.03142f); await _streamWriter.Received().WriteLineAsync("SHIFT_POSE:ROLL,0.03142"); } @@ -164,7 +133,7 @@ public async Task ShiftPose_Sample_SendsCorrectly() [TestMethod] public async Task SetArmMaxVelocity_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("SET_ARM_MAX_VELOCITY:OK"); + _streamReader.ReadLineAsync().Returns("SET_ARM_MAX_VELOCITY:OK"); await _connection.SetArmMaxVelocity(50); await _streamWriter.Received().WriteLineAsync("SET_ARM_MAX_VELOCITY:50"); } @@ -172,7 +141,7 @@ public async Task SetArmMaxVelocity_Sample_SendsCorrectly() [TestMethod] public async Task EnableJoystick_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("ENABLE_JOYSTICK:OK"); + _streamReader.ReadLineAsync().Returns("ENABLE_JOYSTICK:OK"); await _connection.EnableJoystick(false); await _streamWriter.Received().WriteLineAsync("ENABLE_JOYSTICK:FALSE"); } @@ -180,7 +149,7 @@ public async Task EnableJoystick_Sample_SendsCorrectly() [TestMethod] public async Task SetPinMode_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("SET_PIN_MODE:OK"); + _streamReader.ReadLineAsync().Returns("SET_PIN_MODE:OK"); await _connection.SetPinMode(RobotPin.GPIO_2B, PinMode.OUTPUT); await _streamWriter.Received().WriteLineAsync("SET_PIN_MODE:GPIO_2B,OUTPUT"); } @@ -188,7 +157,7 @@ public async Task SetPinMode_Sample_SendsCorrectly() [TestMethod] public async Task DigitalWrite_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("DIGITAL_WRITE:OK"); + _streamReader.ReadLineAsync().Returns("DIGITAL_WRITE:OK"); await _connection.DigitalWrite(RobotPin.GPIO_2A, DigitalState.LOW); await _streamWriter.Received().WriteLineAsync("DIGITAL_WRITE:GPIO_2A,LOW"); } @@ -196,7 +165,7 @@ public async Task DigitalWrite_Sample_SendsCorrectly() [TestMethod] public async Task DigitalRead_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("DIGITAL_READ:OK,HIGH"); + _streamReader.ReadLineAsync().Returns("DIGITAL_READ:OK,HIGH"); await _connection.DigitalRead(RobotPin.GPIO_1A); await _streamWriter.Received().WriteLineAsync("DIGITAL_READ:GPIO_1A"); } @@ -204,7 +173,7 @@ public async Task DigitalRead_Sample_SendsCorrectly() [TestMethod] public async Task ChangeTool_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("CHANGE_TOOL:OK"); + _streamReader.ReadLineAsync().Returns("CHANGE_TOOL:OK"); await _connection.ChangeTool(RobotTool.GRIPPER_2); await _streamWriter.Received().WriteLineAsync("CHANGE_TOOL:GRIPPER_2"); } @@ -212,7 +181,7 @@ public async Task ChangeTool_Sample_SendsCorrectly() [TestMethod] public async Task OpenGripper_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("OPEN_GRIPPER:OK"); + _streamReader.ReadLineAsync().Returns("OPEN_GRIPPER:OK"); await _connection.OpenGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("OPEN_GRIPPER:GRIPPER_1,200"); } @@ -220,7 +189,7 @@ public async Task OpenGripper_Sample_SendsCorrectly() [TestMethod] public async Task CloseGripper_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("CLOSE_GRIPPER:OK"); + _streamReader.ReadLineAsync().Returns("CLOSE_GRIPPER:OK"); await _connection.CloseGripper(RobotTool.GRIPPER_1, 200); await _streamWriter.Received().WriteLineAsync("CLOSE_GRIPPER:GRIPPER_1,200"); } @@ -228,7 +197,7 @@ public async Task CloseGripper_Sample_SendsCorrectly() [TestMethod] public async Task PullAirVacuumPump_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("PULL_AIR_VACUUM_PUMP:OK"); + _streamReader.ReadLineAsync().Returns("PULL_AIR_VACUUM_PUMP:OK"); await _connection.PullAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PULL_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -236,7 +205,7 @@ public async Task PullAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task PushAirVacuumPump_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("PUSH_AIR_VACUUM_PUMP:OK"); + _streamReader.ReadLineAsync().Returns("PUSH_AIR_VACUUM_PUMP:OK"); await _connection.PushAirVacuumPump(RobotTool.VACUUM_PUMP_1); await _streamWriter.Received().WriteLineAsync("PUSH_AIR_VACUUM_PUMP:VACUUM_PUMP_1"); } @@ -244,7 +213,7 @@ public async Task PushAirVacuumPump_Sample_SendsCorrectly() [TestMethod] public async Task SetupElectromagnet_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("SETUP_ELECTROMAGNET:OK"); + _streamReader.ReadLineAsync().Returns("SETUP_ELECTROMAGNET:OK"); await _connection.SetupElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("SETUP_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -252,7 +221,7 @@ public async Task SetupElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task ActivateElectromagnet_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("ACTIVATE_ELECTROMAGNET:OK"); + _streamReader.ReadLineAsync().Returns("ACTIVATE_ELECTROMAGNET:OK"); await _connection.ActivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2B); await _streamWriter.Received().WriteLineAsync("ACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2B"); } @@ -260,7 +229,7 @@ public async Task ActivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task DeactivateElectromagnet_Sample_SendsCorrectly() { - SetupReadAsyncReturn_("DEACTIVATE_ELECTROMAGNET:OK"); + _streamReader.ReadLineAsync().Returns("DEACTIVATE_ELECTROMAGNET:OK"); await _connection.DeactivateElectromagnet(RobotTool.ELECTROMAGNET_1, RobotPin.GPIO_2C); await _streamWriter.Received().WriteLineAsync("DEACTIVATE_ELECTROMAGNET:ELECTROMAGNET_1,GPIO_2C"); } @@ -268,7 +237,7 @@ public async Task DeactivateElectromagnet_Sample_SendsCorrectly() [TestMethod] public async Task GetJoints_Sample_Works() { - SetupReadAsyncReturn_("GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0"); + _streamReader.ReadLineAsync().Returns("GET_JOINTS:OK,0.0,0.640187,-1.397485,0.0,0.0,0.0"); var joints = await _connection.GetJoints(); CollectionAssert.AreEqual(new[] { 0.0f, 0.640187f, -1.397485f, 0.0f, 0.0f, 0.0f }, joints.ToArray()); } @@ -276,7 +245,7 @@ public async Task GetJoints_Sample_Works() [TestMethod] public async Task GetPose_Sample_Works() { - SetupReadAsyncReturn_("GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12"); + _streamReader.ReadLineAsync().Returns("GET_POSE:OK,0.0695735635306,1.31094787803e-12,0.200777981243,-5.10302119597e-12,0.757298,5.10351727471e-12"); var pose = await _connection.GetPose(); CollectionAssert.AreEqual(new[] { 0.0695735635306f, 1.31094787803e-12f, 0.200777981243f, -5.10302119597e-12f, 0.757298f, 5.10351727471e-12f }, pose.ToArray()); @@ -285,7 +254,7 @@ public async Task GetPose_Sample_Works() [TestMethod] public async Task GetHardwareStatus_Sample_Works() { - SetupReadAsyncReturn_("GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)"); + _streamReader.ReadLineAsync().Returns("GET_HARDWARE_STATUS:OK,59,2,True,'',0,False,['Stepper Axis 1', 'Stepper Axis 2', 'Stepper Axis 3', 'Servo Axis 4', 'Servo Axis 5', 'Servo Axis 6'],['Niryo Stepper', 'Niryo Stepper', 'Niryo Stepper', 'DXL XL-430', 'DXL XL-430', 'DXL XL-320'],(34, 34, 37, 43, 45, 37),(0.0, 0.0, 0.0, 11.3, 11.2, 7.9),(0, 0, 0, 0, 0, 0)"); var status = await _connection.GetHardwareStatus(); Assert.AreEqual(59, status.RpiTemperature); Assert.AreEqual(2, status.HardwareVersion); @@ -303,7 +272,7 @@ public async Task GetHardwareStatus_Sample_Works() [TestMethod] public async Task GetLearningMode_Sample_Works() { - SetupReadAsyncReturn_("GET_LEARNING_MODE:OK,FALSE"); + _streamReader.ReadLineAsync().Returns("GET_LEARNING_MODE:OK,FALSE"); var mode = await _connection.GetLearningMode(); await _streamWriter.Received().WriteLineAsync("GET_LEARNING_MODE"); Assert.AreEqual(false, mode); @@ -312,7 +281,7 @@ public async Task GetLearningMode_Sample_Works() [TestMethod] public async Task GetDigitalIOState_Sample_Works() { - SetupReadAsyncReturn_("GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]"); + _streamReader.ReadLineAsync().Returns("GET_DIGITAL_IO_STATE:OK,[2, '1A', 1, 1],[3, '1B', 1, 1],[16, '1C', 1, 1],[26, '2A', 1, 1],[19, '2B', 1, 1],[6, '2C', 1, 1],[12, 'SW1', 0, 0],[13, 'SW2', 0, 0]"); var state = await _connection.GetDigitalIOState(); Assert.AreEqual(2, state[0].PinId); Assert.AreEqual("1A", state[0].Name); @@ -355,33 +324,10 @@ public async Task GetDigitalIOState_Sample_Works() Assert.AreEqual(DigitalState.LOW, state[7].State); } - [TestMethod] - public async Task ReceiveAnswerAsync_GroupedInOneRead_Works() { - SetupReadAsyncReturn_("HEJ:OKBLA:OKMERP:OK"); - var a1 = await _connection.ReceiveAnswerAsync("HEJ"); - Assert.AreEqual("", a1); - var a2 = await _connection.ReceiveAnswerAsync("BLA"); - Assert.AreEqual("", a2); - var a3 = await _connection.ReceiveAnswerAsync("MERP"); - Assert.AreEqual("", a3); - } - - [TestMethod] - public async Task ReceiveAnswerAsync_SplitInMultipleReads_Works() - { - SetupReadAsyncReturn_("HEJ:O", "KBLA:", "OKMERP", ":OK"); - var a1 = await _connection.ReceiveAnswerAsync("HEJ"); - Assert.AreEqual("", a1); - var a2 = await _connection.ReceiveAnswerAsync("BLA"); - Assert.AreEqual("", a2); - var a3 = await _connection.ReceiveAnswerAsync("MERP"); - Assert.AreEqual("", a3); - } - [TestMethod] public async Task ReceiveAnswerAsync_ArbitraryWhitespaceBetween_Works() { - SetupReadAsyncReturn_("HEJ:OK\n\t BLA:OK \t\r\nMERP:OK\r\n"); + _streamReader.ReadLineAsync().Returns("HEJ:OK", "\t BLA:OK \t", "\nMERP:OK\r\n"); var a1 = await _connection.ReceiveAnswerAsync("HEJ"); Assert.AreEqual("", a1); var a2 = await _connection.ReceiveAnswerAsync("BLA"); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index 38be4af7..e839a155 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -64,12 +63,9 @@ internal async Task WriteLineAsync(string s) /// Read response data from the tcp server /// /// The string read - internal async Task ReadAsync() + internal async Task ReadLineAsync() { - const int blockSize = 512; - Memory memory = new Memory(new char[blockSize]); - var count = await _textReader.ReadAsync(memory); - return new string(memory.Span.Slice(0, count).ToArray()); + return await _textReader.ReadLineAsync(); } /// @@ -87,38 +83,29 @@ protected async Task SendCommandAsync(string commandType, params string[] args) await WriteLineAsync(cmd); } - private string _stringBuf = ""; - /// /// Receive an answer from the tcp server related to a previously sent command /// /// The command for which a response is expected - /// Optionally, the regular expression that the successful response arguments - /// are supposed to match /// The data portion of the response - internal async Task ReceiveAnswerAsync(string commandType, string regex = "") + internal async Task ReceiveAnswerAsync(string commandType) { - var fullRegex = new Regex($"^[A-Z_]+:(OK{regex}|KO,.*)"); - string s = _stringBuf; - var sb = new StringBuilder(s); - while (!fullRegex.IsMatch(s)) - { - sb.Append(await ReadAsync()); - s = sb.ToString(); - } - var match = fullRegex.Match(s); - var result = match.Value.Trim(); - _stringBuf = s.Substring(match.Index + match.Length).TrimStart(); + var result = await ReadLineAsync(); - var colonSplit = result.Split(':', 2); + var colonSplit = result.Trim().Split(':', 2); var cmd = colonSplit[0]; if (cmd != commandType) throw new NiryoOneException("Wrong command response received."); var commaSplit2 = colonSplit[1].Split(',', 2); var status = commaSplit2[0]; if (status != "OK") - throw new NiryoOneException(commaSplit2[1]); + { + var reason = commaSplit2[1]; + if (reason.StartsWith('"') && reason.EndsWith('"')) + reason = reason.Substring(1, reason.Length - 2); + throw new NiryoOneException(reason); + } if (commaSplit2.Length > 1) return commaSplit2[1]; @@ -220,7 +207,7 @@ public async Task DigitalWrite(RobotPin pin, DigitalState state) public async Task DigitalRead(RobotPin pin) { await SendCommandAsync("DIGITAL_READ", pin.ToString()); - var state = await ReceiveAnswerAsync("DIGITAL_READ", ",(0|1|HIGH|LOW)"); + var state = await ReceiveAnswerAsync("DIGITAL_READ"); return (DigitalState)Enum.Parse(typeof(DigitalState), state); } @@ -314,7 +301,7 @@ public async Task DeactivateElectromagnet(RobotTool tool, RobotPin pin) public async Task GetJoints() { await SendCommandAsync("GET_JOINTS"); - var joints = await ReceiveAnswerAsync("GET_JOINTS", "(, *[-0-9.e]+){6}"); + var joints = await ReceiveAnswerAsync("GET_JOINTS"); return ParserUtils.ParseRobotJoints(joints); } @@ -324,7 +311,7 @@ public async Task GetJoints() public async Task GetPose() { await SendCommandAsync("GET_POSE"); - var pose = await ReceiveAnswerAsync("GET_POSE", "(, *[-0-9.e]+){6}"); + var pose = await ReceiveAnswerAsync("GET_POSE"); return ParserUtils.ParsePoseObject(pose); } @@ -334,8 +321,7 @@ public async Task GetPose() public async Task GetHardwareStatus() { await SendCommandAsync("GET_HARDWARE_STATUS"); - var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS", - @"(, *([^,\[\]()]+|\[[^\[\]()]*\]|\([^\[\]()]*\))){11}"); + var status = await ReceiveAnswerAsync("GET_HARDWARE_STATUS"); return ParserUtils.ParseHardwareStatus(status); } @@ -345,7 +331,7 @@ public async Task GetHardwareStatus() public async Task GetLearningMode() { await SendCommandAsync("GET_LEARNING_MODE"); - var mode = await ReceiveAnswerAsync("GET_LEARNING_MODE", ", *(TRUE|FALSE)"); + var mode = await ReceiveAnswerAsync("GET_LEARNING_MODE"); return bool.Parse(mode); } @@ -355,7 +341,7 @@ public async Task GetLearningMode() public async Task GetDigitalIOState() { await SendCommandAsync("GET_DIGITAL_IO_STATE"); - var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE", @"(, *\[[^]]*\]){8}"); + var state = await ReceiveAnswerAsync("GET_DIGITAL_IO_STATE"); var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); var matches = regex.Matches(state); From 76532482d2be1e467406fe7cad8d83cb7a80b3b4 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Tue, 3 Dec 2019 22:33:25 +0100 Subject: [PATCH 34/38] Update csharp client to use line-based tcp protocol --- .../NiryoOneConnectionTest.cs | 2 +- .../NiryoOneClient/NiryoOneClient.csproj | 2 +- .../NiryoOneClient/NiryoOneConnection.cs | 14 +++++------ .../csharp/NiryoOneClient/ParserUtils.cs | 23 ++++++++++++------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs index 42e49a71..3efcd23f 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneConnectionTest.cs @@ -96,7 +96,7 @@ public async Task SetLearningMode_WrongResponse_Throws() [TestMethod] public async Task SetLearningMode_ErrorResponse_Throws() { - _streamReader.ReadLineAsync().Returns("SET_LEARNING_MODE:KO,No good"); + _streamReader.ReadLineAsync().Returns("SET_LEARNING_MODE:KO,\"No good\""); var e = await Assert.ThrowsExceptionAsync(async () => await _connection.SetLearningMode(false)); Assert.AreEqual("No good", e.Reason); await _streamWriter.Received().WriteLineAsync("SET_LEARNING_MODE:FALSE"); diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj index 0db46329..8b5c2b0f 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netstandard2.0 diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs index e839a155..4ba39f02 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneConnection.cs @@ -93,17 +93,15 @@ internal async Task ReceiveAnswerAsync(string commandType) var result = await ReadLineAsync(); - var colonSplit = result.Trim().Split(':', 2); + var colonSplit = result.Trim().Split(new[] {':'}, 2); var cmd = colonSplit[0]; if (cmd != commandType) throw new NiryoOneException("Wrong command response received."); - var commaSplit2 = colonSplit[1].Split(',', 2); + var commaSplit2 = colonSplit[1].Split(new[] {','}, 2); var status = commaSplit2[0]; if (status != "OK") { - var reason = commaSplit2[1]; - if (reason.StartsWith('"') && reason.EndsWith('"')) - reason = reason.Substring(1, reason.Length - 2); + var reason = ParserUtils.Strip(commaSplit2[1], "\"", "\""); throw new NiryoOneException(reason); } @@ -139,7 +137,7 @@ public async Task SetLearningMode(bool mode) /// public async Task MoveJoints(RobotJoints joints) { - await SendCommandAsync("MOVE_JOINTS", string.Join(',', joints.Select(x => x.ToString(CultureInfo.InvariantCulture)))); + await SendCommandAsync("MOVE_JOINTS", string.Join(",", joints.Select(x => x.ToString(CultureInfo.InvariantCulture)))); await ReceiveAnswerAsync("MOVE_JOINTS"); } @@ -149,7 +147,7 @@ public async Task MoveJoints(RobotJoints joints) /// public async Task MovePose(PoseObject pose) { - await SendCommandAsync("MOVE_POSE", string.Join(',', pose.Select(x => x.ToString(CultureInfo.InvariantCulture)))); + await SendCommandAsync("MOVE_POSE", string.Join(",", pose.Select(x => x.ToString(CultureInfo.InvariantCulture)))); await ReceiveAnswerAsync("MOVE_POSE"); } @@ -346,7 +344,7 @@ public async Task GetDigitalIOState() var regex = new Regex("\\[[0-9]+, '[^']*', [0-9]+, [0-9+]\\]"); var matches = regex.Matches(state); - return matches.Select(m => ParserUtils.ParseDigitalPinObject(m.Value)).ToArray(); + return matches.Cast().Select(m => ParserUtils.ParseDigitalPinObject(m.Value)).ToArray(); } } } diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs index 17523565..bfd40459 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/ParserUtils.cs @@ -51,7 +51,7 @@ public static HardwareStatus ParseHardwareStatus(string data) var rpiTemperature = int.Parse(matches[0].Value); var hardwareVersion = int.Parse(matches[1].Value); var connectionUp = bool.Parse(matches[2].Value); - var errorMessage = Strip_(matches[3].Value, '\'', '\''); + var errorMessage = Strip(matches[3].Value, "'", "'"); var calibrationNeeded = int.Parse(matches[4].Value); var calibrationInProgress = bool.Parse(matches[5].Value); @@ -87,7 +87,7 @@ public static HardwareStatus ParseHardwareStatus(string data) /// A parsed object public static PoseObject ParsePoseObject(string s) { - return new PoseObject(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); + return new PoseObject(s.Split(',').Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); } @@ -98,7 +98,7 @@ public static PoseObject ParsePoseObject(string s) /// A parsed object public static RobotJoints ParseRobotJoints(string s) { - return new RobotJoints(s.Split(",").Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); + return new RobotJoints(s.Split(',').Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray()); } @@ -109,10 +109,10 @@ public static RobotJoints ParseRobotJoints(string s) /// A parsed object public static DigitalPinObject ParseDigitalPinObject(string s) { - if (!s.StartsWith('[') || !s.EndsWith(']')) + if (!s.StartsWith("[") || !s.EndsWith("]")) throw new ArgumentException(); - var ss = s.Substring(1, s.Length - 2).Split(", "); + var ss = s.Substring(1, s.Length - 2).Split(',').Select(x => x.Trim()).ToArray(); return new DigitalPinObject { @@ -123,7 +123,14 @@ public static DigitalPinObject ParseDigitalPinObject(string s) }; } - private static string Strip_(string s, char prefix, char suffix) + /// + /// Require s to start with the supplied prefix and end with the supplied suffix. Return s without the prefix and suffix. + /// + /// The initial string + /// The prefix to remove + /// The suffix to remove + /// The supplied string s without prefix and suffix + public static string Strip(string s, string prefix, string suffix) { if (!s.StartsWith(prefix)) throw new ArgumentException(); @@ -135,13 +142,13 @@ private static string Strip_(string s, char prefix, char suffix) private static string[] ParseStrings_(string s) { var regex = new Regex(@"'[^']*'"); - return regex.Matches(s).Select(m => Strip_(m.Value, '\'', '\'')).ToArray(); + return regex.Matches(s).Cast().Select(m => Strip(m.Value, "'", "'")).ToArray(); } private static T[] ParseNumbers_(string s, Func parser) { var regex = new Regex(@"[0-9]+(\.[0-9]*)?"); - return regex.Matches(s).Select(m => parser(m.Value)).ToArray(); + return regex.Matches(s).Cast().Select(m => parser(m.Value)).ToArray(); } } } \ No newline at end of file From 37d7ea5890699d101915e36c681b6511deabfefa Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Tue, 3 Dec 2019 22:39:44 +0100 Subject: [PATCH 35/38] Don't crash when disposing client prior to connection, and downgrade to net core 2.0 --- .../clients/csharp/Examples/Examples.csproj | 2 +- .../NiryoOneClient.Tests.csproj | 18 +++++++++--------- .../csharp/NiryoOneClient/NiryoOneClient.cs | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj index 48ee88a9..841b5b47 100644 --- a/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj +++ b/niryo_one_tcp_server/clients/csharp/Examples/Examples.csproj @@ -6,7 +6,7 @@ Exe - netcoreapp3.0 + netcoreapp2.0 false diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj index c62b56ee..a5e11f83 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient.Tests/NiryoOneClient.Tests.csproj @@ -1,28 +1,28 @@ - netcoreapp3.0 + netcoreapp2.0 false - - runtime; build; native; contentfiles; analyzers; buildtransitive - all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all - - runtime; build; native; contentfiles; analyzers; buildtransitive - all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all - - + + diff --git a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs index 6fb2ac25..5f0df59d 100644 --- a/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs +++ b/niryo_one_tcp_server/clients/csharp/NiryoOneClient/NiryoOneClient.cs @@ -73,12 +73,18 @@ public async Task Connect() /// public void Dispose() { - _stream.Close(); - _stream.Dispose(); - _stream = null; - _client.Close(); - _client.Dispose(); - _client = null; + if (_stream != null) + { + _stream.Close(); + _stream.Dispose(); + _stream = null; + } + if (_client != null) + { + _client.Close(); + _client.Dispose(); + _client = null; + } } } } \ No newline at end of file From cebb0b27fd2a354a62f4e41fcf909edcdbab0d2e Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Tue, 3 Dec 2019 22:43:49 +0100 Subject: [PATCH 36/38] Correct requirements in README --- niryo_one_tcp_server/clients/csharp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/README.md b/niryo_one_tcp_server/clients/csharp/README.md index 41e92990..be8bf941 100644 --- a/niryo_one_tcp_server/clients/csharp/README.md +++ b/niryo_one_tcp_server/clients/csharp/README.md @@ -8,7 +8,7 @@ Port of the server: 40001 ## Building -* The target framework for the API is .NET Core 3.0. With the SDK for that installed, just run the command `dotnet pack` in the csharp directory, and a NuGet package will be produced in the directory `NiryoOneClient/bin/Release`. +* The target framework for the API is .NET Core 2.0. With the SDK for that installed, just run the command `dotnet pack` in the csharp directory, and a NuGet package will be produced in the directory `NiryoOneClient/bin/Release`. ## Examples From c09c003712a0ca32911d2bec8b4b1f8cc36f24fb Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 4 Dec 2019 08:43:13 +0100 Subject: [PATCH 37/38] Correct documentation --- niryo_one_tcp_server/clients/csharp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/README.md b/niryo_one_tcp_server/clients/csharp/README.md index be8bf941..6eae40b3 100644 --- a/niryo_one_tcp_server/clients/csharp/README.md +++ b/niryo_one_tcp_server/clients/csharp/README.md @@ -8,7 +8,7 @@ Port of the server: 40001 ## Building -* The target framework for the API is .NET Core 2.0. With the SDK for that installed, just run the command `dotnet pack` in the csharp directory, and a NuGet package will be produced in the directory `NiryoOneClient/bin/Release`. +* The target framework for the API is .NET Core 2.0. With the SDK for that installed, just run the command `dotnet pack --configuration Release --include-symbols` in the csharp directory, and a NuGet package will be produced in the directory `NiryoOneClient/bin/Release` along with a separate debug symbols package. Alternatively, in Visual Studio Code, just run the task "pack", which will do the same thing. ## Examples From c2dc9824c0ea4a302991808d12dc1044c732ffd2 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 4 Dec 2019 08:45:18 +0100 Subject: [PATCH 38/38] More correct doc --- niryo_one_tcp_server/clients/csharp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niryo_one_tcp_server/clients/csharp/README.md b/niryo_one_tcp_server/clients/csharp/README.md index 6eae40b3..053559fa 100644 --- a/niryo_one_tcp_server/clients/csharp/README.md +++ b/niryo_one_tcp_server/clients/csharp/README.md @@ -20,4 +20,4 @@ After running `dotnet publish`, xml documentation will be generated at [NiryoOne ## Tests -Running the command `dotnet test` will compile and run the unit tests available. \ No newline at end of file +Running the command `dotnet test` will compile and run the unit tests available. Running the Visual Studio Code task "test" will also produce a test coverage report. \ No newline at end of file