### Run Go-only Loopback Example Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Start a Go-only loopback test by running the passive equipment and active host examples. This is a quick way to test basic functionality. ```bash go run ./example/example1_passive_equipment ``` ```bash go run ./example/example2_active_host --scenario all ``` -------------------------------- ### Install Go SECS-II/HSMS Library Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md Use 'go get' to install the library. Ensure you have Go installed and configured. ```bash go get github.com/wolimst/lib-secs2-hsms-go ``` -------------------------------- ### Complete GEM Host Example in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Demonstrates a GEM host application connecting to equipment, querying variables, setting up reports, and sending commands. Requires setup of HSMS protocol and GEM handler with event callbacks. ```go package main import ( "log" "os" "os/signal" "syscall" "time" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/hsms" ) func main() { protocol := hsms.NewHsmsProtocol("127.0.0.1", 5000, true, 0xFFFF, "host") protocol.Timeouts().SetLinktest(30) handler, err := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceHost, Logging: gem.LoggingOptions{ Enabled: true, Mode: hsms.LoggingModeSML, }, }) if err != nil { log.Fatal(err) } defer handler.Disable() // Event callbacks handler.Events().EventReportReceived.AddCallback(func(data map[string]interface{}) { if rpt, ok := data["report"].(gem.EventReport); ok { log.Printf("S6F11: CEID=%v", rpt.CEID) } }) handler.Events().AlarmReceived.AddCallback(func(data map[string]interface{}) { if alarm, ok := data["alarm"].(gem.AlarmEvent); ok { log.Printf("Alarm: %s (set=%v)", alarm.Text, alarm.Set) } }) handler.Enable() log.Println("Connecting to equipment...") if !handler.WaitForCommunicating(30 * time.Second) { log.Fatal("Timeout connecting") } log.Println("Connected") // Query status variables infos, _ := handler.RequestStatusVariableInfo() for _, info := range infos { log.Printf("SVID %v: %s (%s)", info.ID, info.Name, info.Unit) } // Set up report workflow handler.DefineReports() // Clear handler.DefineReports(gem.ReportDefinitionRequest{ ReportID: 4001, VIDs: []interface{}{int64(1001), int64(2001)}, }) handler.LinkEventReports(gem.EventReportLinkRequest{ CEID: int64(3001), ReportIDs: []interface{}{int64(4001)}, }) handler.EnableEventReports(true, int64(3001)) // Send command result, _ := handler.SendRemoteCommand("START", nil) log.Printf("START command: HCACK=%d", result.HCACK) sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig log.Println("Disconnecting") } ``` -------------------------------- ### Complete GEM Equipment Example in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt A full GEM-compliant equipment application demonstrating status variables, data variables, collection events, alarms, and remote command handling. This example sets up an HSMS protocol and a GEM handler, registering various GEM entities and event callbacks. ```go package main import ( "log" "math/rand" "os" "os/signal" "sync/atomic" "syscall" "time" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/hsms" "github.com/younglifestyle/secs4go/lib-secs2-hsms-go/pkg/ast" ) func main() { protocol := hsms.NewHsmsProtocol("127.0.0.1", 5000, false, 0x200, "equipment") protocol.Timeouts().SetLinktest(30) handler, err := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceEquipment, MDLN: "Demo-Equipment", SOFTREV: "1.0.0", InitialControlState: gem.ControlStateEquipmentOffline, Logging: gem.LoggingOptions{ Enabled: true, Mode: hsms.LoggingModeSML, }, }) if err != nil { log.Fatal(err) } defer handler.Disable() // Register variables var temp uint32 = 25 tempVar, _ := gem.NewStatusVariable(1001, "Temperature", "C", gem.WithStatusValueProvider(func() (ast.ItemNode, error) { return ast.NewUintNode(4, int(atomic.LoadUint32(&temp))), nil })) handler.RegisterStatusVariable(tempVar) pressureVar, _ := gem.NewDataVariable(2001, "Pressure", gem.WithDataValueProvider(func() (ast.ItemNode, error) { return ast.NewUintNode(4, 900+rand.Intn(50)), nil })) handler.RegisterDataVariable(pressureVar) ce, _ := gem.NewCollectionEvent(3001, "StatusSnapshot") handler.RegisterCollectionEvent(ce) handler.RegisterAlarm(gem.Alarm{ID: 5001, Text: "Temperature High"}) handler.SetRemoteCommandHandler(func(req gem.RemoteCommandRequest) (gem.RemoteCommandResult, error) { log.Printf("Command: %s", req.Command) if req.Command == "START" || req.Command == "STOP" { return gem.RemoteCommandResult{HCACK: gem.HCACKAcknowledge}, nil } return gem.RemoteCommandResult{HCACK: gem.HCACKInvalidCommand}, nil }) handler.Events().HandlerCommunicating.AddCallback(func(_ map[string]interface{}) { log.Println("Equipment communicating") }) handler.Enable() log.Println("Equipment listening on :5000") // Periodic event trigger go func() { ticker := time.NewTicker(10 * time.Second) for range ticker.C { if handler.State() == gem.CommunicationStateCommunicating { handler.TriggerCollectionEvent(int64(3001)) } } }() sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig log.Println("Shutting down") } ``` -------------------------------- ### Run Go GEM Equipment Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Starts the Go equipment sample with specified network parameters. ```bash go run ./secs4go/example/geminterop --mode equipment --addr 127.0.0.1 --port 5000 --session 0x100 ``` -------------------------------- ### Run Interop with Python (secsgem) Example Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Set up interoperability testing between secs4go and Python's secsgem library. This involves running both an equipment and a host application. ```bash python samples/gem_equipment.py ``` ```bash go run example/gem_pytest/main.go --role host ``` -------------------------------- ### Run Go GEM Host Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Starts the Go host sample to interact with equipment on the specified address and port. ```bash go run ./secs4go/example/geminterop --mode host --addr 127.0.0.1 --port 5000 --session 0x100 ``` -------------------------------- ### Run Python GEM Host Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Starts the Python secsgem host implementation for interoperability testing. ```bash python -m secsgem.examples.gem_host --port 5000 ``` -------------------------------- ### Run Python GEM Equipment Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Starts the Python secsgem equipment implementation for interoperability testing. ```bash python -m secsgem.examples.gem_equipment --port 5000 ``` -------------------------------- ### Quick Start: Initialize and Connect GEM Handler Source: https://github.com/younglifestyle/secs4go/blob/master/README.md This snippet demonstrates the basic steps to initialize a GEM handler and establish a communication session. ```APIDOC ## Quick Start: Initialize and Connect GEM Handler ### Description This example shows how to set up the GEM options, create a new GEM handler, enable it, and wait for the communication to be established. ### Method N/A (Initialization code) ### Endpoint N/A ### Request Body N/A ### Request Example ```go opts := gem.Options{ Protocol: hsms.NewHsmsProtocol("127.0.0.1", 5000, true, 0xFFFF, "sample"), DeviceType: gem.DeviceHost, } handler, err := gem.NewGemHandler(opts) if err != nil { log.Fatal(err) } handler.Enable() if handler.WaitForCommunicating(30 * time.Second) { log.Println("GEM session established") } ``` ### Response N/A (This is client-side initialization code) ``` -------------------------------- ### Enable HSMS/GEM Traffic Logging in Go Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Configure logging options for HSMS/GEM traffic by enabling detailed wire logging. This setup allows for inspection of SML and/or hexadecimal HSMS frames. ```go handler, err := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceEquipment, Logging: gem.LoggingOptions{ Enabled: true, Mode: hsms.LoggingModeBoth, // SML + hex IncludeControlMessages: false, // hide select/linktest }, }) ``` -------------------------------- ### Parse HSMS Byte Sequence Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md The HSMS parser converts raw byte sequences into SECS-II/HSMS message objects. This example shows parsing a byte sequence representing a linktest.req control message. ```text byte sequence 00 00 00 0A FF FF 00 00 00 05 FF FF FF FF will be parsed to a ControlMessage that represent linktest.req. ``` -------------------------------- ### Configure Logging Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Demonstrates how to configure standard console logging and structured file logging with rotation using zap and lumberjack. ```go opts := gem.Options{ // ... Logger: gem.NewStdLogger(os.Stdout, "gem: "), } ``` ```go // 1. App Logger (Info/Error/Debug messages) appLogger := gem.NewZapLogger(gem.ZapLoggerOptions{ LogFile: "app.log", MaxSize: 10, // MB MaxBackups: 5, DebugLevel: true, }) opts := gem.Options{ // ... Logger: appLogger, // 2. Protocol Trace Logging (SML/Binary) Logging: gem.LoggingOptions{ Enabled: true, LogFile: "protocol.log", // Separate rotation for SML traces MaxSize: 100, // MB MaxBackups: 10, }, } ``` -------------------------------- ### Upload and Download Process Programs with Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Manages process programs using S7F3 for uploads and S7F5 for downloads. Requires a valid GemHandler instance. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Host handler // Upload a process program to equipment (S7F3/S7F4) programBody := "STEP1:TEMP=100,TIME=30;STEP2:TEMP=150,TIME=45;END" ack, err := handler.UploadProcessProgram("RECIPE-001", programBody) if err != nil { log.Printf("UploadProcessProgram error: %v", err) return } log.Printf("Upload acknowledgement: PPGNT=%d", ack) // Download a process program from equipment (S7F5/S7F6) body, ack, err := handler.RequestProcessProgram("RECIPE-001") if err != nil { log.Printf("RequestProcessProgram error: %v", err) return } log.Printf("Download acknowledgement: ACKC7=%d", ack) log.Printf("Program body: %s", body) } ``` -------------------------------- ### Initialize GEM Handler Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Sets up the GEM handler with HSMS protocol options and waits for the communication session to be established. ```go opts := gem.Options{ Protocol: hsms.NewHsmsProtocol("127.0.0.1", 5000, true, 0xFFFF, "sample"), DeviceType: gem.DeviceHost, } handler, err := gem.NewGemHandler(opts) if err != nil { log.Fatal(err) } handler.Enable() if handler.WaitForCommunicating(30 * time.Second) { log.Println("GEM session established") } ``` -------------------------------- ### Initialize GEM Handler Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Wraps the HSMS protocol in a GEM handler. Requires specifying the device type and optional logging configurations. ```go handler, err := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceEquipment, MDLN: "demo-eqp", SOFTREV: "1.0.0", Logging: gem.LoggingOptions{ // optional, see section 7 Enabled: true, Mode: hsms.LoggingModeSML, IncludeControlMessages: false, }, }) if err != nil { log.Fatalf("create handler: %v", err) } ``` -------------------------------- ### Registering Process Programs Source: https://context7.com/younglifestyle/secs4go/llms.txt Configures default process programs and custom handlers for upload and request operations. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Register a default process program handler.RegisterProcessProgram("SAMPLE", "RECIPE-STEP1;TEMP=100;TIME=30") handler.RegisterProcessProgram("CLEAN", "CLEAN-STEP1;PURGE;CLEAN-STEP2;RINSE") // Custom upload handler (host sends S7F3) handler.SetProcessProgramUploadHandler(func(ppid interface{}, body string) int { log.Printf("Received process program upload: PPID=%v", ppid) log.Printf("Program body: %s", body) // Validate the program if len(body) == 0 { return 1 // PPACK = 1 (permission denied) } // Store the program (handler stores automatically, this is for custom logic) handler.RegisterProcessProgram(ppid, body) return 0 // PPACK = 0 (accepted) }) // Custom request handler (host sends S7F5) handler.SetProcessProgramRequestHandler(func(ppid interface{}) (body string, ack int) { log.Printf("Process program request: PPID=%v", ppid) // Return program body and acknowledgement // ack = 0 (success), non-zero for errors return "RECIPE-DATA-HERE", 0 }) log.Println("Process programs configured") } ``` -------------------------------- ### Run Go GEM Tests Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Executes the Go test suite to verify GEM feature implementation. ```bash go test ./secs4go/gem ``` -------------------------------- ### Run secs4go Interoperability Tests Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Execute the built-in tests for the secs4go project to verify interoperability. Ensure you are in the correct directory before running the command. ```bash cd secs4go go test ./... ``` -------------------------------- ### Define and Link Host Reports Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Configures report definitions and links them to specific collection events. Always clear existing definitions before defining new ones to avoid stale state. ```go handler.DefineReports() // S2F33 clear handler.DefineReports(gem.ReportDefinitionRequest{ ReportID: 4001, VIDs: []interface{}{1011, 2001}, }) handler.LinkEventReports(gem.EventReportLinkRequest{CEID: 3001, ReportIDs: []interface{}{4001}}) handler.EnableEventReports(true, 3001) ``` -------------------------------- ### Create HSMS Protocol Connections Source: https://context7.com/younglifestyle/secs4go/llms.txt Use `hsms.NewHsmsProtocol` to create active (client) or passive (server) HSMS protocol instances. Configure connection details, session IDs, and timeouts for link testing and reply notifications. ```go package main import ( "log" "github.com/younglifestyle/secs4go/hsms" ) func main() { // Create active (client) HSMS protocol for host hostProtocol := hsms.NewHsmsProtocol( "127.0.0.1", // Remote address 5000, // Port true, // Active mode (client) 0xFFFF, // Session ID (0xFFFF allows wildcard) "my-host", // Name for logging ) // Create passive (server) HSMS protocol for equipment equipmentProtocol := hsms.NewHsmsProtocol( "127.0.0.1", 5000, false, // Passive mode (server) 0x200, // Fixed session ID "my-equipment", ) // Configure timeouts hostProtocol.Timeouts().SetLinktest(30) // Linktest interval in seconds hostProtocol.Timeouts().SetT3ReplyTimeout(45) // Reply timeout hostProtocol.Timeouts().SetT7NotSelectTimeout(10) // Not-selected timeout hostProtocol.Timeouts().SetT8NetworkIntercharTimeout(45) // Network timeout log.Println("HSMS protocols configured") } ``` -------------------------------- ### Register and Trigger Collection Events Source: https://context7.com/younglifestyle/secs4go/llms.txt Use NewCollectionEvent and RegisterCollectionEvent to define events, then TriggerCollectionEvent to send notifications to the host. Ensure the handler state is Communicating before triggering. ```go package main import ( "log" "time" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Register a collection event statusEvent, err := gem.NewCollectionEvent( 3001, // CEID "StatusSnapshot", // Name ) if err != nil { log.Fatalf("create collection event: %v", err) } if err := handler.RegisterCollectionEvent(statusEvent); err != nil { log.Fatalf("register collection event: %v", err) } // Register additional events alarmEvent, _ := gem.NewCollectionEvent(3002, "AlarmTriggered") handler.RegisterCollectionEvent(alarmEvent) processComplete, _ := gem.NewCollectionEvent(3003, "ProcessComplete") handler.RegisterCollectionEvent(processComplete) // Trigger collection event periodically (only if reports are linked by host) go func() { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for range ticker.C { if handler.State() == gem.CommunicationStateCommunicating { if err := handler.TriggerCollectionEvent(int64(3001)); err != nil { log.Printf("trigger event error: %v", err) } } } }() log.Println("Collection events registered") } ``` -------------------------------- ### Logging Configuration: Structured File Logging Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Configure the GEM library for structured file logging with rotation using `go.uber.org/zap` and `lumberjack.v2`. ```APIDOC ## Logging Configuration: Structured File Logging ### Description This example shows how to set up structured file logging for both application messages and protocol traces, including log rotation. ### Method N/A (Configuration code) ### Endpoint N/A ### Parameters #### Request Body ```json { "gem.Options": { "Logger": "gem.NewZapLogger(gem.ZapLoggerOptions{...})", "Logging": { "Enabled": true, "LogFile": "protocol.log", "MaxSize": 100, "MaxBackups": 10 } } } ``` ### Request Example ```go // 1. App Logger (Info/Error/Debug messages) appLogger := gem.NewZapLogger(gem.ZapLoggerOptions{ LogFile: "app.log", MaxSize: 10, // MB MaxBackups: 5, DebugLevel: true, }) opts := gem.Options{ // ... other options Logger: appLogger, // 2. Protocol Trace Logging (SML/Binary) Logging: gem.LoggingOptions{ Enabled: true, LogFile: "protocol.log", // Separate rotation for SML traces MaxSize: 100, // MB MaxBackups: 10, }, } ``` ### Response N/A ``` -------------------------------- ### Send Remote Commands with Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Executes remote commands on equipment using S2F41/S2F42. Supports both simple commands and those requiring parameter lists. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Host handler // Simple command without parameters result, err := handler.SendRemoteCommand("START", nil) if err != nil { log.Printf("SendRemoteCommand error: %v", err) return } switch result.HCACK { case gem.HCACKAcknowledge: log.Println("Command acknowledged successfully") case gem.HCACKAcknowledgeLater: log.Println("Command acknowledged, will complete later") case gem.HCACKInvalidCommand: log.Println("Invalid command") case gem.HCACKCannotPerformNow: log.Println("Cannot perform now") case gem.HCACKParameterInvalid: log.Println("Parameter invalid") for _, pAck := range result.ParameterAcks { log.Printf(" Parameter %v: CPACK=%d", pAck.Name, pAck.Ack) } default: log.Printf("Command returned HCACK=%d", result.HCACK) } // Command with parameters params := []gem.RemoteCommandParameterValue{ {Name: "PPID", Value: "RECIPE-001"}, {Name: "LOT_ID", Value: "LOT-12345"}, {Name: "COUNT", Value: uint32(100)}, } result, err = handler.SendRemoteCommand("PP-SELECT", params) if err != nil { log.Printf("SendRemoteCommand error: %v", err) return } log.Printf("PP-SELECT result: HCACK=%d", result.HCACK) } ``` -------------------------------- ### Configure and Manage Alarms Source: https://context7.com/younglifestyle/secs4go/llms.txt Register alarms using RegisterAlarm, then use RaiseAlarm and ClearAlarm to manage equipment conditions. Raising an alarm triggers an S5F1 message to the host. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Register alarms handler.RegisterAlarm(gem.Alarm{ ID: 1001, Text: "Temperature High", Enabled: true, }) handler.RegisterAlarm(gem.Alarm{ ID: 1002, Text: "Pressure Low", Enabled: true, }) handler.RegisterAlarm(gem.Alarm{ ID: 1003, Text: "Door Open", Enabled: true, }) // Raise an alarm (sends S5F1 to host) if err := handler.RaiseAlarm(1001, true); err != nil { log.Printf("raise alarm error: %v", err) } // Clear an alarm if err := handler.ClearAlarm(1001); err != nil { log.Printf("clear alarm error: %v", err) } log.Println("Alarms configured") } ``` -------------------------------- ### Create GEM Handler Source: https://context7.com/younglifestyle/secs4go/llms.txt Instantiate a GEM handler using `gem.NewGemHandler` by providing an HSMS protocol and GEM-specific options. This includes device type, model/revision, communication wait times, initial control state, online mode, and logging configurations. The handler must be enabled and can be disabled later. ```go package main import ( "log" "os" "time" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/hsms" ) func main() { protocol := hsms.NewHsmsProtocol("127.0.0.1", 5000, false, 0x200, "equipment") handler, err := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceEquipment, // or gem.DeviceHost DeviceID: 0x200, MDLN: "MyEquipment", // Model name (equipment only) SOFTREV: "1.0.0", // Software revision (equipment only) EstablishCommunicationWait: 10 * time.Second, InitialControlState: gem.ControlStateEquipmentOffline, InitialOnlineMode: gem.OnlineModeRemote, Logging: gem.LoggingOptions{ Enabled: true, Mode: hsms.LoggingModeSML, // or LoggingModeBinary, LoggingModeBoth IncludeControlMessages: true, LogFile: "protocol.log", // Optional file rotation MaxSize: 100, // MB before rotation MaxBackups: 5, }, Logger: gem.NewStdLogger(os.Stdout, "gem: "), // Optional custom logger }) if err != nil { log.Fatalf("create handler: %v", err) } handler.Enable() defer handler.Disable() if handler.WaitForCommunicating(30 * time.Second) { log.Println("GEM session established, state:", handler.State()) log.Println("Control state:", handler.ControlState()) } else { log.Println("Timeout waiting for communication") } } ``` -------------------------------- ### Initialize HSMS Protocol Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Creates an HSMS protocol instance for communication. The active parameter determines if the instance acts as a client (true) or server (false). ```go protocol := hsms.NewHsmsProtocol("127.0.0.1", 5000, false, 0x200, "eqp") protocol.Timeouts().SetLinktest(30) // optional overrides ``` -------------------------------- ### Logging Configuration: Standard Console Logging Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Configure the GEM library to use standard console logging. ```APIDOC ## Logging Configuration: Standard Console Logging ### Description This example demonstrates how to configure the GEM library to output logs to the standard console using `gem.NewStdLogger`. ### Method N/A (Configuration code) ### Endpoint N/A ### Parameters #### Request Body ```json { "gem.Options": { "Logger": "gem.NewStdLogger(os.Stdout, \"gem: \")" } } ``` ### Request Example ```go opts := gem.Options{ // ... other options Logger: gem.NewStdLogger(os.Stdout, "gem: "), } ``` ### Response N/A ``` -------------------------------- ### Register Equipment Constants (ECIDs) in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Use gem.NewEquipmentConstant to define configurable parameters. Supports bounds checking, custom validation, and update callbacks. ```go package main import ( "errors" "log" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/lib-secs2-hsms-go/pkg/ast" ) func main() { var handler *gem.GemHandler speedConstant, err := gem.NewEquipmentConstant( 3001, // ECID "ProcessSpeed", // Name ast.NewUintNode(4, 100), // Default value (required) gem.WithEquipmentConstantUnit("rpm"), gem.WithEquipmentConstantMin(ast.NewUintNode(4, 50)), gem.WithEquipmentConstantMax(ast.NewUintNode(4, 500)), gem.WithEquipmentConstantValidator(func(node ast.ItemNode) error { // Custom validation logic if uint, ok := node.(*ast.UintNode); ok { if values, ok := uint.Values().([]uint64); ok && len(values) > 0 { if values[0] < 50 || values[0] > 500 { return errors.New("speed must be between 50-500 rpm") } } } return nil }), gem.WithEquipmentConstantValueUpdater(func(node ast.ItemNode) error { log.Printf("ProcessSpeed updated to: %v", node) // Apply the new value to actual equipment return nil }), ) if err != nil { log.Fatalf("create equipment constant: %v", err) } if err := handler.RegisterEquipmentConstant(speedConstant); err != nil { log.Fatalf("register equipment constant: %v", err) } log.Println("Equipment constants registered") } ``` -------------------------------- ### Defining and Linking Reports Source: https://context7.com/younglifestyle/secs4go/llms.txt Configures report definitions, links them to collection events, and enables event reporting for S6F11 notifications. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" ) func main() { var handler *gem.GemHandler // Host handler // Step 1: Clear existing report definitions (S2F33 with empty list) ack, err := handler.DefineReports() if err != nil { log.Printf("Clear reports error: %v", err) return } log.Printf("Clear reports DRACK=%d", ack) // Step 2: Define new reports (S2F33) report := gem.ReportDefinitionRequest{ ReportID: 4001, VIDs: []interface{}{int64(1001), int64(2001)}, // SVID and VID } ack, err = handler.DefineReports(report) if err != nil { log.Printf("DefineReports error: %v", err) return } log.Printf("DefineReports DRACK=%d", ack) // Step 3: Link reports to collection events (S2F35) link := gem.EventReportLinkRequest{ CEID: int64(3001), ReportIDs: []interface{}{int64(4001)}, } ack, err = handler.LinkEventReports(link) if err != nil { log.Printf("LinkEventReports error: %v", err) return } log.Printf("LinkEventReports LRACK=%d", ack) // Step 4: Enable event reporting (S2F37) ack, err = handler.EnableEventReports(true, int64(3001)) if err != nil { log.Printf("EnableEventReports error: %v", err) return } log.Printf("EnableEventReports ERACK=%d", ack) // Step 5: Request immediate snapshot (S6F15/S6F16) snapshot, err := handler.RequestCollectionEventReport(int64(3001)) if err != nil { log.Printf("RequestCollectionEventReport error: %v", err) return } log.Printf("Snapshot CEID=%v reports=%d", snapshot.CEID, len(snapshot.Reports)) } ``` -------------------------------- ### GEM Capabilities: Remote Command Support Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Explains how to handle remote commands sent from the host. ```APIDOC ## GEM Capabilities: Remote Command Support ### Description This outlines the mechanism for supporting remote commands. The host can send commands using `SendRemoteCommand` (which translates to S2F41/S2F42), and the equipment can define how to handle these commands by implementing the `SetRemoteCommandHandler` hook. ### Method N/A (Function calls for remote command handling) ### Endpoint N/A ### Parameters #### Equipment Side - `SetRemoteCommandHandler(func(command string, args map[string]interface{}) (interface{}, error))`: Hook to process incoming remote commands. #### Host Side - `SendRemoteCommand(command string, args map[string]interface{}) (RemoteCommandResult, error)`: Sends a remote command to the equipment. ### Request Example (Equipment Side) ```go handler.SetRemoteCommandHandler(func(command string, args map[string]interface{}) (interface{}, error) { // Process the command and arguments log.Printf("Received remote command: %s with args: %v", command, args) // Return a result or error return "Command processed successfully", nil }) ``` ### Response N/A ``` -------------------------------- ### Subscribe to GEM Events with Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Registers callbacks for monitoring communication states, control states, alarms, event reports, and S9 errors. Callbacks receive data via a map interface. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/hsms" ) func main() { var handler *gem.GemHandler // Communication state changed handler.Events().HandlerCommunicating.AddCallback(func(data map[string]interface{}) { log.Println("GEM handler is now communicating") }) // Control state changed (equipment only) handler.Events().ControlStateChanged.AddCallback(func(data map[string]interface{}) { prev := data["previous"].(gem.ControlState) next := data["current"].(gem.ControlState) mode := data["mode"].(gem.OnlineControlMode) log.Printf("Control state: %s -> %s (mode=%s)", prev, next, mode) }) // Alarm received (host only) handler.Events().AlarmReceived.AddCallback(func(data map[string]interface{}) { if alarm, ok := data["alarm"].(gem.AlarmEvent); ok { log.Printf("Alarm: ID=%d Text=%s Set=%v", alarm.ID, alarm.Text, alarm.Set) } }) // S6F11 Event report received (host only) handler.Events().EventReportReceived.AddCallback(func(data map[string]interface{}) { if report, ok := data["report"].(gem.EventReport); ok { log.Printf("Event Report: CEID=%v DATAID=%d", report.CEID, report.DATAID) for _, rpt := range report.Reports { log.Printf(" Report ID=%v, Values=%d", rpt.ReportID, len(rpt.Values)) } } }) // S9 Error received handler.Events().S9ErrorReceived.AddCallback(func(data map[string]interface{}) { if info, ok := data["error"].(*hsms.S9ErrorInfo); ok { log.Printf("S9 Error: Code=%d Text=%s", info.ErrorCode, info.ErrorText) } }) // Remote command received (equipment only) handler.Events().RemoteCommandReceived.AddCallback(func(data map[string]interface{}) { if req, ok := data["request"].(gem.RemoteCommandRequest); ok { log.Printf("Remote command event: %s", req.Command) } }) } ``` -------------------------------- ### Register Status Variables (SVIDs) in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Use gem.NewStatusVariable to define equipment state values. Supports dynamic providers via WithStatusValueProvider or static values via WithStatusValue. ```go package main import ( "log" "sync/atomic" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/lib-secs2-hsms-go/pkg/ast" ) func main() { // Assume handler is already created with DeviceType: gem.DeviceEquipment var handler *gem.GemHandler // Dynamic status variable with provider callback var temperature uint32 = 25 tempVar, err := gem.NewStatusVariable( 1001, // SVID (integer or string) "Temperature", // Name "C", // Unit gem.WithStatusValueProvider(func() (ast.ItemNode, error) { val := atomic.LoadUint32(&temperature) return ast.NewUintNode(4, int(val)), nil }), ) if err != nil { log.Fatalf("create status variable: %v", err) } if err := handler.RegisterStatusVariable(tempVar); err != nil { log.Fatalf("register status variable: %v", err) } // Static status variable modelVar, _ := gem.NewStatusVariable( 1002, "ModelName", "", gem.WithStatusValue(ast.NewASCIINode("Equipment-Model-X")), ) handler.RegisterStatusVariable(modelVar) // Update temperature dynamically atomic.StoreUint32(&temperature, 30) log.Println("Status variables registered") } ``` -------------------------------- ### SML Syntax: Line Comment Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md Use '//' to add line comments. Comments are ignored unless they are within an ASCII quoted string. ```text S1F13 W // This is comment > . ``` -------------------------------- ### GEM Capabilities: Alarm Reporting Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Details on how to implement alarm reporting using the GEM handler. ```APIDOC ## GEM Capabilities: Alarm Reporting ### Description This section describes how to manage alarms within the GEM framework. Alarms can be registered using `RegisterAlarm` and then triggered or cleared using `RaiseAlarm` and `ClearAlarm`, which correspond to S5F1/S5F2 SECS messages. ### Method N/A (Function calls for alarm management) ### Endpoint N/A ### Parameters #### Functions - `RegisterAlarm(alarmID, alarmText)`: Registers an alarm. - `RaiseAlarm(alarmID)`: Triggers an alarm (sends S5F1). - `ClearAlarm(alarmID)`: Clears an active alarm (sends S5F2). ### Request Example ```go // Register an alarm handler.RegisterAlarm(1, "Equipment Malfunction") // Raise the alarm handler.RaiseAlarm(1) // Clear the alarm handler.ClearAlarm(1) ``` ### Response N/A ``` -------------------------------- ### Register Equipment Status Variable Source: https://github.com/younglifestyle/secs4go/blob/master/using-secs4go.md Defines a status variable with a provider function to supply values. This is used to report equipment state to the host. ```go sv, _ := gem.NewStatusVariable(1001, "Temperature", "C", gem.WithStatusValueProvider(func() (ast.ItemNode, error) { return ast.NewUintNode(4, 25), nil }), ) handler.RegisterStatusVariable(sv) ``` -------------------------------- ### SML Syntax: Arbitrary Data Item Size Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md Define data item sizes using exact values, ranges, or open-ended ranges for flexibility. ```text S5F11 W // size should be exactly 32 (SML default syntax) // size should be in range of 5..20 (SML default syntax) // size lower limit is 0, upper limit is 5 // size lower limit is 5, upper limit is not specified > . ``` -------------------------------- ### Run Python GEM Tests Source: https://github.com/younglifestyle/secs4go/blob/master/interoperability.md Executes the Python secsgem test suite for GEM functionality verification. ```bash pytest tests/test_gem_equipment_handler.py -k "status or report or process" ``` -------------------------------- ### Register Data Variables (VIDs) in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Use gem.NewDataVariable for process data values used in event reporting. These function similarly to status variables but are specifically for event-based data. ```go package main import ( "log" "math/rand" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/lib-secs2-hsms-go/pkg/ast" ) func main() { var handler *gem.GemHandler // Data variable with dynamic provider pressureVar, err := gem.NewDataVariable( 2001, // VID "Pressure", // Name gem.WithDataValueProvider(func() (ast.ItemNode, error) { value := 900 + rand.Intn(40) // Simulated pressure reading return ast.NewUintNode(4, value), nil }), ) if err != nil { log.Fatalf("create data variable: %v", err) } if err := handler.RegisterDataVariable(pressureVar); err != nil { log.Fatalf("register data variable: %v", err) } // Static data variable batchIDVar, _ := gem.NewDataVariable( 2002, "BatchID", gem.WithDataValue(ast.NewASCIINode("BATCH-001")), ) handler.RegisterDataVariable(batchIDVar) log.Println("Data variables registered") } ``` -------------------------------- ### Register Custom SECS-II Stream Function Handlers in Go Source: https://context7.com/younglifestyle/secs4go/llms.txt Register custom handlers for specific SECS-II message streams and functions. This allows for custom logic when certain messages are received. A default handler can also be registered for unhandled messages, and handlers can be removed. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/lib-secs2-hsms-go/pkg/ast" ) func main() { var handler *gem.GemHandler // Register handler for S10F3 (Terminal Display, Single) handler.RegisterStreamFunctionHandler(10, 3, func(msg *ast.DataMessage) (*ast.DataMessage, error) { log.Printf("Received S10F3: %s", msg.String()) // Extract terminal ID and text termID, _ := msg.Get(0) text, _ := msg.Get(1) log.Printf("Terminal %v: %v", termID, text) // Return S10F4 acknowledgement ackBody := ast.NewBinaryNode(0) // ACKC10 = 0 (accepted) return ast.NewDataMessage("TerminalAck", 10, 4, 0, "H<-E", ackBody), nil }) // Register default handler for unhandled messages handler.RegisterDefaultStreamHandler(func(msg *ast.DataMessage) (*ast.DataMessage, error) { log.Printf("Unhandled message: S%dF%d", msg.StreamCode(), msg.FunctionCode()) // Return nil to indicate no response needed return nil, nil }) // Remove a handler handler.RegisterStreamFunctionHandler(10, 3, nil) log.Println("Custom handlers registered") } ``` -------------------------------- ### Parse SML String in Go Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md Import the necessary packages and use the sml.Parse function to convert an SML string into message objects. Handles messages, errors, and warnings. ```go import ( "github.com/wolimst/lib-secs2-hsms-go/pkg/ast" "github.com/wolimst/lib-secs2-hsms-go/pkg/parser/sml" ) func main() { messages, errors, warnings := sml.Parse("S1F1 W\n.") // ... } ``` -------------------------------- ### Requesting Status Variable Information Source: https://context7.com/younglifestyle/secs4go/llms.txt Queries equipment for status variable metadata and current values using S1F11/S1F12 and S1F3/S1F4 messages. ```go package main import ( "log" "github.com/younglifestyle/secs4go/gem" "github.com/younglifestyle/secs4go/hsms" ) func main() { protocol := hsms.NewHsmsProtocol("127.0.0.1", 5000, true, 0xFFFF, "host") handler, _ := gem.NewGemHandler(gem.Options{ Protocol: protocol, DeviceType: gem.DeviceHost, }) handler.Enable() handler.WaitForCommunicating(30 * 1e9) // Request all status variable information (S1F11/S1F12) infos, err := handler.RequestStatusVariableInfo() if err != nil { log.Printf("RequestStatusVariableInfo error: %v", err) return } for _, info := range infos { log.Printf("SVID=%v NAME=%s UNIT=%s", info.ID, info.Name, info.Unit) } // Request specific status variable values (S1F3/S1F4) values, err := handler.RequestStatusVariables(int64(1001), int64(1002), int64(1003)) if err != nil { log.Printf("RequestStatusVariables error: %v", err) return } for _, v := range values { log.Printf("SVID=%v VALUE=%v", v.ID, v.Value) } } ``` -------------------------------- ### Error Handling: S9 Error Notification Source: https://github.com/younglifestyle/secs4go/blob/master/README.md Demonstrates how to register a callback to receive notifications for S9 'Error' messages. ```APIDOC ## Error Handling: S9 Error Notification ### Description This example shows how to subscribe to the `S9ErrorReceived` event to be notified when the GEM handler encounters protocol violations and automatically generates S9 error messages. ### Method N/A (Event handling code) ### Endpoint N/A ### Request Body N/A ### Request Example ```go handler.Events().S9ErrorReceived.AddCallback(func(data map[string]interface{}) { if info, ok := data["error"].(*hsms.S9ErrorInfo); ok { log.Printf("S9 Error: %s", info.ErrorText) } }) ``` ### Response N/A ``` -------------------------------- ### SML Syntax: Message Name Source: https://github.com/younglifestyle/secs4go/blob/master/lib-secs2-hsms-go/README.md Optionally specify a message name after the direction or wait bit. Message names can include Unicode characters except whitespace. ```text S1F1 W H->E AreYouThere? . ```