Initial commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Sick.ColaB.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for ColaB commands
|
||||
/// </summary>
|
||||
public abstract class CommandBase(string commandString)
|
||||
{
|
||||
private readonly string _commandString = commandString ?? throw new ArgumentNullException(nameof(commandString));
|
||||
private bool _wasSuccessful;
|
||||
|
||||
public ReadOnlyMemory<byte> GetCommandData()
|
||||
{
|
||||
return Encoding.ASCII.GetBytes(_commandString);
|
||||
}
|
||||
|
||||
public bool WasSuccessful
|
||||
{
|
||||
get => _wasSuccessful;
|
||||
protected set => _wasSuccessful = value;
|
||||
}
|
||||
|
||||
public abstract bool ProcessReply(ReadOnlyMemory<byte> replyData);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command string
|
||||
/// </summary>
|
||||
protected string CommandString => _commandString;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Sick.ColaB.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to call a method on the scanner (sMN - Method by Name)
|
||||
/// </summary>
|
||||
public sealed class MethodCommand(string methodName, params string[] parameters) : CommandBase($"sMN {methodName} {string.Join(" ", parameters)}")
|
||||
{
|
||||
public string MethodName { get; } = methodName;
|
||||
public string[] Parameters { get; } = parameters;
|
||||
|
||||
public string? ReplyValue { get; private set; }
|
||||
|
||||
public override bool ProcessReply(ReadOnlyMemory<byte> replyData)
|
||||
{
|
||||
// Parse reply: "sAN <methodName> <result>" or "sMA <methodName> <result>" or "sFA <errorCode>" (Error)
|
||||
var replyString = Encoding.ASCII.GetString(replyData.Span);
|
||||
|
||||
// Check if reply starts with "sFA" (Error) - scanner rejected the command
|
||||
if (replyString.StartsWith("sFA", StringComparison.Ordinal))
|
||||
{
|
||||
// Extract error code if present
|
||||
var errorParts = replyString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
var errorInfo = errorParts.Length > 1 ? $" (error code: {string.Join(" ", errorParts.Skip(1))})" : "";
|
||||
WasSuccessful = false;
|
||||
ReplyValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if reply starts with "sAN" (Answer) or "sMA" (Method Answer)
|
||||
if (!replyString.StartsWith("sAN", StringComparison.Ordinal) &&
|
||||
!replyString.StartsWith("sMA", StringComparison.Ordinal))
|
||||
{
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract result after method name
|
||||
var parts = replyString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length >= 2 && parts[1] == MethodName)
|
||||
{
|
||||
if (parts.Length > 2)
|
||||
{
|
||||
ReplyValue = string.Join(" ", parts.Skip(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplyValue = string.Empty;
|
||||
}
|
||||
WasSuccessful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Sick.ColaB.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to read a value from the scanner (sRN - Read by Name)
|
||||
/// </summary>
|
||||
public sealed class ReadCommand(string variableName) : CommandBase($"sRN {variableName}")
|
||||
{
|
||||
public string VariableName { get; } = variableName;
|
||||
|
||||
public string? ReplyValue { get; private set; }
|
||||
|
||||
public byte[]? RawReplyData { get; private set; }
|
||||
|
||||
public override bool ProcessReply(ReadOnlyMemory<byte> replyData)
|
||||
{
|
||||
// Store raw reply data for binary parsing
|
||||
RawReplyData = replyData.ToArray();
|
||||
|
||||
// Parse reply: "sAN <variableName> <value>" or "sRA <variableName> <value>"
|
||||
// For binary data, only the header is ASCII, the rest is binary
|
||||
// Try to find where ASCII header ends
|
||||
var asciiHeaderEnd = -1;
|
||||
for (int i = 0; i < replyData.Length && i < 100; i++)
|
||||
{
|
||||
if (replyData.Span[i] == 0 || replyData.Span[i] > 127)
|
||||
{
|
||||
// Found non-ASCII byte, header ends here
|
||||
asciiHeaderEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found where ASCII ends, only decode that part
|
||||
var headerLength = asciiHeaderEnd > 0 ? asciiHeaderEnd : Math.Min(replyData.Length, 100);
|
||||
var replyString = Encoding.ASCII.GetString(replyData.Span[..headerLength]);
|
||||
|
||||
// Check if reply starts with "sAN" (Answer) or "sRA" (Read Answer)
|
||||
if (!replyString.StartsWith("sAN", StringComparison.Ordinal) &&
|
||||
!replyString.StartsWith("sRA", StringComparison.Ordinal))
|
||||
{
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract value after variable name
|
||||
var parts = replyString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length >= 2 && parts[1] == VariableName)
|
||||
{
|
||||
// For binary data, ReplyValue will contain ASCII header only
|
||||
// The actual binary data is in RawReplyData
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
ReplyValue = string.Join(" ", parts.Skip(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Binary data follows immediately after header
|
||||
ReplyValue = string.Empty;
|
||||
}
|
||||
WasSuccessful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Sick.ColaB.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command to write a value to the scanner (sWN - Write by Name)
|
||||
/// </summary>
|
||||
public sealed class WriteCommand(string variableName, string value) : CommandBase($"sWN {variableName} {value}")
|
||||
{
|
||||
public string VariableName { get; } = variableName;
|
||||
public string Value { get; } = value;
|
||||
|
||||
public override bool ProcessReply(ReadOnlyMemory<byte> replyData)
|
||||
{
|
||||
// Parse reply: "sWA <variableName>" (Write Acknowledge), "sEA <eventName> <value>" (Event Acknowledge), "sAN <methodName> <result>" (Answer), or "sFA <errorCode>" (Error)
|
||||
var replyString = Encoding.ASCII.GetString(replyData.Span);
|
||||
|
||||
// Check if reply starts with "sFA" (Error) - this indicates command failed
|
||||
if (replyString.StartsWith("sFA", StringComparison.Ordinal))
|
||||
{
|
||||
// Extract error code if present
|
||||
var parts = replyString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
var errorInfo = parts.Length > 1 ? $" (error code: {string.Join(" ", parts.Skip(1))})" : "";
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if reply starts with "sWA" (Write Acknowledge), "sEA" (Event Acknowledge), or "sAN" (Answer)
|
||||
if (replyString.StartsWith("sWA", StringComparison.Ordinal) ||
|
||||
replyString.StartsWith("sEA", StringComparison.Ordinal) ||
|
||||
replyString.StartsWith("sAN", StringComparison.Ordinal))
|
||||
{
|
||||
WasSuccessful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
WasSuccessful = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user