Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add diagnostic configuration stages to device availability check #10

Merged
merged 6 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading