Install Secs4Net via NuGet
baseTo use Secs4Net in your .NET project, install the NuGet package using the dotnet CLI.
dotnet add package Secs4Netrepository·base·Indexed 20 days ago
https://github.com/mkjeff/secs4net.NET implementation of SECS-II, HSMS-SS, and GEM standards for high-performance communication with semiconductor equipment. Features include support for SecsMessage and SecsItem hierarchies, .NET Dependency Injection integration via AddSecs4Net, and memory-efficient handling of large data using IMemoryOwner.
To use Secs4Net in your .NET project, install the NuGet package using the dotnet CLI.
dotnet add package Secs4NetThe SecsDevice sample program allows you to simulate either a device or a host to communicate with other HSMS-SS/SECS-II compliant devices. It serves as a demonstration of how to send and reply to messages using the SECS-II protocol.
To interact with the demo, you can use SML (SECS Message Language) format to define messages. Ensure you include the closing angle brackets for list items.
S6F11ReadyToLoad: 'S6F11' W
<L [3]
<U4 [1] 320 >
<U2 [1] 114 >
<L [1]
<L [2]
<U2 [1] 500 >
<L [1]
<U1 [1] 1 >
>
>
>
>
.For large unmanaged data, you can create an Item directly from IMemoryOwner<T> or Memory<T>. This is more efficient than allocating new arrays. Note that IMemoryOwner<T>, Item, and SecsMessage all implement IDisposable; you must dispose of them to return the memory to the pool.
// Using Microsoft.Toolkit.HighPerformance
var largeArrayOwner = MemoryOwner<int>.Allocate(size: 65535);
FillLargeArray(largeArrayOwner.Memory);
using var s6f11 = new SecsMessage(6, 11, replyExpected: false)
{
Name = "LargeDataEvent",
SecsItem = L(
L(
I2(1121),
A(""),
I4(largeArrayOwner)), // Create Item from IMemoryOwner
);
}
// When receiving large messages, the decoded items also use MemoryOwner
using var s6f12 = await secsGem.SendAsync(s6f11);Register Secs4Net in your service collection using AddSecs4Net<TLogger>(IConfiguration). The configuration should be provided via an appsettings.json file under a secs4net section. You must implement the ISecsGemLogger interface to handle logging.
// appsettings.json configuration example:
// "secs4net": {
// "DeviceId": 0,
// "IsActive": true,
// "IpAddress": "127.0.0.1",
// "Port": 5000
// }
public void ConfigureServices(IServiceCollection services)
{
services.AddSecs4Net<DeviceLogger>(Configuration);
}
class DeviceLogger : ISecsGemLogger
{
// implement ISecsGemLogger methods
}Use the SecsMessage constructor to define the Stream and Function (e.g., 3, 17). You can build the SecsItem hierarchy using helper methods like L (List), U4 (Unsigned 4-byte), A (ASCII string), B (Binary), and Boolean. Use secsGem.SendAsync(message) to send the message and await the secondary response.
try
{
var s3f17 = new SecsMessage(3, 17)
{
Name = "CreateProcessJob",
SecsItem = L(
U4(0),
L(
L(
A("Id"),
B(0x0D),
L(
A("carrier id"),
L(U1(1)),
L(
U1(1),
A("recipe"),
L()
),
Boolean(true),
L()
)
)
)
)
};
// await the secondary message
var s3f18 = await secsGem.SendAsync(s3f17);
}
catch(SecsException)
{
// Handle T3 timeout, SxF0 reply, or S9Fx reply
}You can use LINQ to transform domain objects into a SecsItem structure. This is particularly useful when mapping complex collections to nested SECS-II lists.
using static Secs4Net.Item;
var s16f15 = new SecsMessage(16, 15)
{
Name = "CreateProcessJob",
SecsItem = L(
U4(0),
L(
from pj in tx.ProcessJobs
select L(
A(pj.Id),
B(0x0D),
L(
from carrier in pj.Carriers
select L(
A(carrier.Id),
L(
from slotInfo in carrier.SlotMap
select U1(slotInfo.SlotNo)
)
),
L(
U1(1),
A(pj.RecipeId),
L()
),
Boolean(true),
L()
)
)
)
)
};To process incoming primary messages from a device, use GetPrimaryMessageAsync. This returns an enumerable of messages that you can iterate through. Use e.TryReplyAsync(secondaryMsg) to send a response back to the device.
await foreach (var e in secsGem.GetPrimaryMessageAsync(cancellationToken))
{
using var primaryMsg = e.PrimaryMessage;
// process primaryMsg
using var secondaryMsg = new SecsMessage(...);
await e.TryReplyAsync(secondaryMsg);
}You can navigate the SecsItem hierarchy using indexers. For unmanaged data, use FirstValue<T>() to retrieve or overwrite values, GetFirstValueOrDefault<T>(fallback) for safe access, and GetMemory<T>() to access the underlying memory. String items can be accessed via GetString().
// Access list items
s3f17.SecsItem[1][0][0] == A("Id");
// Access unmanaged array items
byte b2 = s3f17.SecsItem[0].FirstValue<byte>();
s3f17.SecsItem[0].FirstValue<byte>() = 0; // Overwrite existing memory
byte b3 = s3f17.SecsItem[0].GetFirstValueOrDefault<byte>(fallbackValue);
Memory<byte> bytes = s3f17.SecsItem[0].GetMemory<byte>();
// Access string items
string str = s3f17.SecsItem[1][0][0].GetString();