Skip to content

Commit

Permalink
Merge pull request #10 from graugans/feature/wait
Browse files Browse the repository at this point in the history
feature: add diagnostic configuration stages to device availability check
  • Loading branch information
graugans authored Apr 14, 2024
2 parents dab6132 + c5b1369 commit 603ed91
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
A GO module and cli to access the ifm OVP8xx series of devices.


[![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/gomods/athens.svg)](https://github.com/gomods/athens)
[![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/graugans/go-ovp8xx.svg)](https://github.com/graugans/go-ovp8xx)
[![Go Reference](https://pkg.go.dev/badge/github.com/graugans/go-ovp8xx/v2.svg)](https://pkg.go.dev/github.com/graugans/go-ovp8xx/v2)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
![example workflow](https://github.com/graugans/go-ovp8xx/actions/workflows/go.yml/badge.svg)
[![codecov](https://codecov.io/gh/graugans/go-ovp8xx/graph/badge.svg?token=BU6UPYCUPI)](https://codecov.io/gh/graugans/go-ovp8xx)
Expand Down
6 changes: 5 additions & 1 deletion cmd/ovp8xx/cmd/waitforonline.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ func waitForOnlineCommand(cmd *cobra.Command, args []string) error {
return err
}

if ok, err = o3r.IsAvailable(time.Duration(timeout) * time.Second); err != nil {
timeOutDuration := time.Duration(timeout) * time.Second
if ok, err = o3r.IsAvailable(
timeOutDuration,
ovp8xx.AndPortsAreOnline(),
); err != nil {
return err
}
if ok {
Expand Down
67 changes: 62 additions & 5 deletions pkg/ovp8xx/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package ovp8xx

import (
"context"
"encoding/json"
"fmt"
"slices"
"time"
)

Expand All @@ -23,7 +25,7 @@ type (
// multiple client options.
// Example usage:
//
// client := NewClient(WithTimeout(10 * time.Second), WithRetry(3))
// client := NewClient(WithHost("192.168.47.11"))
// // ...
func NewClient(opts ...ClientOption) *Client {
// Initialise with default values
Expand Down Expand Up @@ -55,22 +57,77 @@ func (device *Client) GetDiagnosticClient() *DiagnosisClient {
return client
}

// WaitForConfig represents the configuration for waiting for a specific stage.
type WaitForConfig struct {
stage string // The stage to wait for.
}

// WaitForOption is a function type that is used as an option for configuring the behavior of the WaitFor function.
// It takes a pointer to a WaitForConfig struct as a parameter and can be used to modify its properties.
type WaitForOption func(c *WaitForConfig)

// AndPortsAreOnline returns a WaitForOption function that sets the stage to "ports".
func AndPortsAreOnline() WaitForOption {
return func(w *WaitForConfig) {
w.stage = "ports"
}
}

// AndAppsAreOnline returns a WaitForOption function that sets the stage to "applications".
func AndAppsAreOnline() WaitForOption {
return func(w *WaitForConfig) {
w.stage = "applications"
}
}

// IsAvailable checks if the client is available by making a XML-RPC get request to query the "/device" object.
// This is useful to wait until a device is ready for communication.
// It returns a boolean indicating the availability status and an error if any.
// The function uses a timeout duration to limit the execution time of the request.
func (d *Client) IsAvailable(timeout time.Duration) (bool, error) {
// Example usage:
//
// ok, err := device.IsAvailable(time.Duration(timeout) * time.Second, ovp8xx.AndPortsAreOnline())
// // ...
func (d *Client) IsAvailable(timeout time.Duration, opts ...WaitForOption) (bool, error) {
var err error
proc := make(chan struct{}, 1)
conf := *NewConfig()

waitingFor := &WaitForConfig{
stage: "device",
}

// Apply wait options
for _, opt := range opts {
opt(waitingFor)
}

// result is a struct that represents the response from the OVP8xx device.
// It contains information about the device's diagnostic data, such as the configuration initialization stages.
result := struct {
Device struct {
Diagnostic struct {
ConfInitStages []string `json:"confInitStages"`
} `json:"diagnostic"`
} `json:"device"`
}{}

go func() {
for {
if _, err := d.Get([]string{"/device"}); err != nil {
if conf, err = d.Get([]string{"/device/diagnostic/confInitStages"}); err != nil {
// In case of an error retry, regardless of an timeout
continue
}
// we are done, the get call was successful
proc <- struct{}{}
// Unmarshal the data into the result struct
if err = json.Unmarshal([]byte(conf.String()), &result); err != nil {
// In case of an error retry until the timeout
continue
}
// Check if the device is ready
if slices.Contains(result.Device.Diagnostic.ConfInitStages, waitingFor.stage) {
// we are done, the get call was successful
proc <- struct{}{}
}
}
}()

Expand Down
5 changes: 4 additions & 1 deletion pkg/ovp8xx/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ func IsTimeoutError(err error) bool {
// Get retrieves the configuration for the specified pointers from the OVP8xx device.
// The pointers parameter is a slice of strings that contains the pointers to retrieve the configuration for.
// The function returns the retrieved configuration as a Config struct and an error if any occurred.
// Example usage: config, err := device.Get([]string{"/device", "/ports"})
// Example usage:
//
// config, err := device.Get([]string{"/device", "/ports"})
// // ...
func (device *Client) Get(pointers []string) (Config, error) {
client, err := xmlrpc.NewClient(device.url)
if err != nil {
Expand Down

0 comments on commit 603ed91

Please sign in to comment.