Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
using System.Text;
using Sick.Tim781s.Colab.Helpers;
namespace Sick.Tim781s.Colab.Commands;
/// <summary>
/// Base class for ColaB commands
/// </summary>
public abstract class CommandBase : IColaBCommand
{
private readonly string _commandString;
private bool _wasSuccessful;
protected CommandBase(string commandString)
{
_commandString = commandString ?? throw new ArgumentNullException(nameof(commandString));
}
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;
}

View File

@@ -0,0 +1,55 @@
using System.Text;
namespace Sick.Tim781s.Colab.Commands;
/// <summary>
/// Command to call a method on the scanner (sMN - Method by Name)
/// </summary>
public sealed class MethodCommand : CommandBase
{
public MethodCommand(string methodName, params string[] parameters)
: base($"sMN {methodName} {string.Join(" ", parameters)}")
{
MethodName = methodName;
Parameters = parameters;
}
public string MethodName { get; }
public string[] Parameters { get; }
public string? ReplyValue { get; private set; }
public override bool ProcessReply(ReadOnlyMemory<byte> replyData)
{
// Parse reply: "sAN <methodName> <result>" or "sMA <methodName> <result>"
var replyString = Encoding.ASCII.GetString(replyData.Span);
// 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;
}
}

View File

@@ -0,0 +1,77 @@
using System.Text;
using Sick.Tim781s.Colab.Helpers;
namespace Sick.Tim781s.Colab.Commands;
/// <summary>
/// Command to read a value from the scanner (sRN - Read by Name)
/// </summary>
public sealed class ReadCommand : CommandBase
{
public ReadCommand(string variableName)
: base($"sRN {variableName}")
{
VariableName = variableName;
}
public string VariableName { get; }
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.Slice(0, 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;
}
}

View File

@@ -0,0 +1,45 @@
using System.Text;
namespace Sick.Tim781s.Colab.Commands;
/// <summary>
/// Command to write a value to the scanner (sWN - Write by Name)
/// </summary>
public sealed class WriteCommand : CommandBase
{
public WriteCommand(string variableName, string value)
: base($"sWN {variableName} {value}")
{
VariableName = variableName;
Value = value;
}
public string VariableName { get; }
public string Value { get; }
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))
{
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;
}
}