This project is a library that implements SECS-II/HSMS in Go.
ToBytes method.ToSML method. Supports various SML formats, including single or double quotes, and handles non-printable ASCII characters.hsms.Connection and hsms.Session interfaces as HSMS-SS, enabling transport-agnostic application code.Refer to the SML document for details.
hsms.Connection and hsms.Session interfaces as hsmsss.The go-secs library provides a structured object representation of HSMS/SECS-II messages and data items using interfaces and concrete types.
secs2.SECS2Message Interface: This interface defines the core components of a SECS-II message, including the stream code, function code, wait bit (W-bit), and the SECS-II data item. The gem package provides functions for generating GEM (Generic Equipment Model) messages that implement this interface.
hsms.HSMSMessage Interface: This interface extends secs2.SECS2Message to include HSMS-specific attributes, such as the session ID, system bytes, and header bytes.
hsms.DataMessage and hsms.ControlMessage: These structs are concrete implementations of the hsms.HSMSMessage interface. hsms.DataMessage represents SECS-II data messages used for exchanging information, while hsms.ControlMessage represents control messages used for managing the HSMS connection.
secs2.Item Interface: This interface provides a unified representation of SECS-II data items. All SECS-II data types (ASCII, Binary, Boolean, Float, Int, Uint, List, JIS-8, Localized String) implement this interface.
Object Hierarchy
hsms.HSMSMessage (interface): embeds secs2.SECS2Message interface
├── hsms.DataMessage
└── hsms.ControlMessage
secs2.Item (interface)
├── ASCIIItem (shortcut: A)
├── BinaryItem (shortcut: B)
├── BooleanItem (shortcut: BOOLEAN)
├── FloatItem (shortcut: F4, F8)
├── IntItem (shortcut: I1, I2, I4, I8)
├── UintItem (shortcut: U1, U2, U4, U8)
└── ListItem (shortcut: L)
go get github.com/arloliu/go-secs
// create a list item
var listItem secs2.Item
listItem = secs2.NewListItem(
// indices: 0
secs2.NewASCIIItem("test1"),
// indices: 1
secs2.NewIntItem(4, 1, 2, 3, 4), // I4/int32 item with 4 values
// indices: 2
secs2.NewIntItem(8, int32[]{1, 2, 3, 4}), // I8/int64 item with 4 values, accepts slice as input
// indices: 3
secs2.NewListItem( // nested list item
// indices: 3, 0
secs2.NewASCIIItem("test2"),
// indices: 3, 1
secs2.NewASCIIItem("test2"),
),
)
// or, create liste item with shortcut function
listItem = secs2.L(
secs2.A("test1"),
secs2.I4(1, 2, 3, 4), // I4/int32 item with 4 values
secs2.I8(int64[]{1, 2, 3, 4}), // I8/int64 item with 4 values, accepts slice as input
secs2.L( // nested list item
secs2.A("test2"),
secs2.A("test3"),
),
)
// Accessing items
item := listItem.Get(0) // "test"
if item.IsASCII() { // should be true
str, err := item.ToASCII() // str == "test1", err == nil
}
// get items in the nested list
item, err := listItem.Get(3) // nested list item
if item.IsList() { // should be true
nestItem, err := item.Get(0) // ascii item "test2"
nestItem, err = item.Get(1) // ascii item "test3"
nestItem, err = item.Get(2) // nil, err == "failed to get nested item"
}
// or, use multiple indices to get nested items
nestItem, err := listItem.Get(3, 0) // ascii item "test2"
nestItem, err = listItem.Get(3, 1) // ascii item "test3"
nestItem, err = listItem.Get(3, 2) // nil, err == "failed to get nested item"
Refer to the SML document for details.
// set stream-function SML string with single quote. e.g. 'S1F1' W <A 'test'>
hsms.UseStreamFunctionSingleQuote()
// set stream-function SML string with double quote. e.g. "S1F1" W <A 'test'>
hsms.UseStreamFunctionDoubleQuote()
// set stream-function SML string without quote. e.g. S1F1 W <A 'test'>
hsms.UseStreamFunctionNoQuote()
// quote ASCII item SML string with double quote. e.g. S1F1 W <A "test">
secs2.UseASCIIDoubleQuote()
// quote ASCII item SML string with double quote. e.g. S1F1 W <A 'test'>
secs2.UseASCIISingleQuote()
Refer to the SML document for details.
// set stream-function SML string with single quote, and quote ASCII item SML string with double quote.
// e.g. 'S1F1' W <A "test">
hsms.UseStreamFunctionSingleQuote()
secs2.UseASCIIDoubleQuote()
sml = `MessageName:'S7F26'
<L[4]
<A "path">
<A "model">
<A "version">
<L[2]
<U4[1] 256>
<A "value">
>
>
.`
msgs, err := sml.ParseHSMS(sml)
if err != nil {
// handle error
}
for _, msg := range msgs {
// msg.Name() == "MessageName"
// msg.StreamCode == uint8(7)
// msg.FunctionCode == uint8(26)
// msg.WaitBit() == false
}
func msgHandler(msg *hsms.DataMessage, session hsms.Session) {
switch msg.StreamCode() {
case 98:
switch msg.FunctionCode() {
case 1:
err := session.ReplyDataMessage(msg, msg.Item())
if err != nil {
// handle reply error
}
}
}
}
func main() {
// ...
// Create a new HSMS-SS connection
connCfg := hsmsss.NewConnectionConfig("127.0.0.1", 5000,
WithActive(), // active mode
WithHostRole(), // host role
WithT3Timeout(30*time.Second),
// other options...
)
conn, err := hsmsss.NewConnection(ctx, connCfg)
if err != nil {
// ... handle error ...
}
defer conn.Close()
// Add a session with session id 1000 before open connection
session := conn.AddSession(1000)
// Add a data message handler
session.AddDataMessageHandler(msgHandler)
// Open connection and wait it to selected state
err = conn.Open(true)
if err != nil {
// ... handle error ...
}
// Send an S99F1 message
reply, err := session.SendDataMessage(1, 1, true, secs2.NewASCIIItem("test"))
if err != nil {
// ... handle error ...
}
// Process the reply
// ... other HSMS-SS operations ...
}
func msgHandler(msg *hsms.DataMessage, session hsms.Session) {
switch msg.StreamCode() {
case 99:
switch msg.FunctionCode() {
case 1:
err := session.ReplyDataMessage(msg, msg.Item())
if err != nil {
// handle reply error
}
}
}
}
func main() {
// ...
// Create a new HSMS-SS connection
connCfg := hsmsss.NewConnectionConfig("127.0.0.1", 5000,
WithPassive(), // passive mode
WithEquipRole(), // equipment role
WithT3Timeout(30*time.Second),
// other options...
)
conn, err := hsmsss.NewConnection(ctx, connCfg)
if err != nil {
// ... handle error ...
}
defer conn.Close()
// Add a session with session id 1000 before open connection
session := conn.AddSession(1000)
// Add a data message handler
session.AddDataMessageHandler(msgHandler)
// Open connection and wait it to selected state
err = conn.Open(true)
if err != nil {
// ... handle error ...
}
// Send an S98F1 message
reply, err := session.SendDataMessage(98, 1, true, secs2.NewASCIIItem("test"))
if err != nil {
// ... handle error ...
}
// Process the reply
// ... other HSMS-SS operations ...
}
```go func msgHandler(msg *hsms.DataMessage, session hsms.Session) { // handle received messages _ = session.ReplyDataMessage(msg, msg.Item()) }
func main() { // ... // Create a new SECS-I connection connCfg, err := secs1.NewConnectionConfig("127.0.0.1", 5000, secs1.WithActive(), // active mode (TCP client) secs1.WithHostRole(), // host role (Slave per SEMI E4 §7.5) secs1.WithDeviceID(1), // 15-bit device ID secs1.WithT3Timeout(30*time.Second), // other options... ) if err != nil { // ... handle error ... }
conn, err := secs1.NewConnection(ctx, connCfg)
if err != nil {
// ... handle error ...
}
defer conn.Close()
// Add a session with device ID before opening connection
session := conn.AddSession(1)
// Add a data message handler
session.AddDataMessageHandler(msgHandler)
// Open connection and wait for selected state
err = conn.Open(true)
if err != nil {
// ... handle error ...
}
// Send an S1F1 message and wait for reply
reply, err := session.SendDataMessage(1, 1, true, secs2.NewASCIIItem("test"))
if err != nil {
$ claude mcp add go-secs \
-- python -m otcore.mcp_server <graph>