refactoring

This commit is contained in:
2025-07-13 06:59:56 +02:00
parent a9f66c9f70
commit 63284ab51c
25 changed files with 158 additions and 105 deletions

View File

@@ -1,13 +0,0 @@
package upnp
func (sv StateValue) Cmp(a, b interface{}) (int, error) {
return sv.valueType.Cmp(a, b)
}
func (sv StateValue) Equal(a, b interface{}) (int, error) {
return sv.valueType.Cmp(a, b)
}
func (sv StateValue) InRange(val interface{}, interval *ValueRange) (bool, error) {
return sv.valueType.InRange(val, interval)
}

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"sync"
@@ -7,8 +7,8 @@ import (
"github.com/beevik/etree"
)
type StateValueInstance struct {
model *StateValue
type StateVarInstance struct {
model *StateVariable
value interface{}
previousValue interface{}
lastChange time.Time
@@ -16,21 +16,21 @@ type StateValueInstance struct {
mu sync.RWMutex
}
func (instance *StateValueInstance) Cast(val interface{}) (interface{}, error) {
func (instance *StateVarInstance) Cast(val interface{}) (interface{}, error) {
return instance.model.Cast(val)
}
func (instance *StateValueInstance) Model() *StateValue {
func (instance *StateVarInstance) Model() *StateVariable {
return instance.model
}
func (instance *StateValueInstance) Value() interface{} {
func (instance *StateVarInstance) Value() interface{} {
instance.mu.RLock()
defer instance.mu.RUnlock()
return instance.value
}
func (instance *StateValueInstance) SetValue(val interface{}) error {
func (instance *StateVarInstance) SetValue(val interface{}) error {
cval, err := instance.Cast(val)
if err != nil {
@@ -44,14 +44,14 @@ func (instance *StateValueInstance) SetValue(val interface{}) error {
return nil
}
func (instance *StateValueInstance) Incr() {
func (instance *StateVarInstance) Incr() {
instance.mu.Lock()
defer instance.mu.Unlock()
}
// ShouldTriggerEvent vérifie toutes les conditions
func (instance *StateValueInstance) ShouldTriggerEvent() bool {
func (instance *StateVarInstance) ShouldTriggerEvent() bool {
for _, condition := range instance.model.eventConditions {
if !condition(instance) {
return false
@@ -60,7 +60,7 @@ func (instance *StateValueInstance) ShouldTriggerEvent() bool {
return true
}
func (sv *StateValueInstance) GenerateEvent() *etree.Element {
func (sv *StateVarInstance) GenerateEvent() *etree.Element {
// Construire le XML d'événement
propSet := etree.NewElement("e:propertyset")

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"encoding/base64"
@@ -17,11 +17,11 @@ import (
type EventType string
type StateConditionFunc func(instance *StateValueInstance) bool
type StateConditionFunc func(instance *StateVarInstance) bool
type StringValueParser func(value string) (interface{}, error)
type ValueSerializer func(value interface{}) (string, error)
type StateValue struct {
type StateVariable struct {
name string // Name of the state value (e.g., "Volume", "Brightness")
valueType StateVarType // Type of state value, see the upnp/statevaluetype package for more information on this.
step interface{} // Step size for incremental state values (e.g., "10")
@@ -40,25 +40,25 @@ type StateValue struct {
// when this type is used in an UPnP State Variable. The returned value can be
// either 8, 16, 24, 32, or 64, depending on whether t is Byte, Boolean, I2, Ui2,
// I4, Ui4 respectively. If none of these types match, it will return -1.
func (sv StateValue) BitSize() int {
func (sv StateVariable) BitSize() int {
return sv.valueType.BitSize()
}
// Name returns the state variable's name (e.g., "Volume", "Brightness").
func (sv StateValue) Name() string {
func (sv StateVariable) Name() string {
return sv.name
}
// Type returns the UPnP data type of the state variable.
func (state *StateValue) Type() StateVarType {
func (state *StateVariable) Type() StateVarType {
return state.valueType
}
func (state *StateValue) AddEventCondition(name string, condition StateConditionFunc) {
func (state *StateVariable) AddEventCondition(name string, condition StateConditionFunc) {
state.eventConditions[name] = condition
}
func (state *StateValue) DeleteEventConditions(name string) error {
func (state *StateVariable) DeleteEventConditions(name string) error {
if _, ok := state.eventConditions[name]; !ok {
return fmt.Errorf("%s: no such event condition (%s)", state.name, name)
}
@@ -67,11 +67,11 @@ func (state *StateValue) DeleteEventConditions(name string) error {
}
// ClearEventConditions réinitialise toutes les conditions
func (state *StateValue) ClearEventConditions() {
func (state *StateVariable) ClearEventConditions() {
state.eventConditions = make(map[string]StateConditionFunc)
}
func (sv *StateValue) SetMinDelta(minDelta interface{}) error {
func (sv *StateVariable) SetMinDelta(minDelta interface{}) error {
if minDelta == nil {
return fmt.Errorf("%s: nil is an invalid minimum delta value", sv.name)
}
@@ -98,7 +98,7 @@ func (sv *StateValue) SetMinDelta(minDelta interface{}) error {
return nil
}
func (state *StateValue) SetDefault(value interface{}) error {
func (state *StateVariable) SetDefault(value interface{}) error {
var err error
var valid bool
@@ -111,11 +111,11 @@ func (state *StateValue) SetDefault(value interface{}) error {
return fmt.Errorf("invalid default value for %v (%v) : %v", state.name, value, err)
}
func (state *StateValue) HasDefault() bool {
func (state *StateVariable) HasDefault() bool {
return state.defaultValue != nil
}
func (state *StateValue) DefaultValue() interface{} {
func (state *StateVariable) DefaultValue() interface{} {
if !state.HasDefault() {
return state.valueType.DefaultValue()
}
@@ -125,13 +125,13 @@ func (state *StateValue) DefaultValue() interface{} {
// HasRange indicates if a value range constraint is defined.
// Returns true if min/max boundaries are set.
func (state *StateValue) HasRange() bool {
func (state *StateVariable) HasRange() bool {
return state.valueRange != nil
}
// Maximum returns the upper bound of the value range.
// Returns nil if no range is defined.
func (state *StateValue) Maximum() interface{} {
func (state *StateVariable) Maximum() interface{} {
if state.valueRange == nil {
return nil
}
@@ -140,7 +140,7 @@ func (state *StateValue) Maximum() interface{} {
// Minimum returns the lower bound of the value range.
// Returns nil if no range is defined.
func (state *StateValue) Minimum() interface{} {
func (state *StateVariable) Minimum() interface{} {
if state.valueRange == nil {
return nil
}
@@ -162,7 +162,7 @@ func (state *StateValue) Minimum() interface{} {
// Example:
//
// err := volumeState.SetRange(0, 100) // 0-100 range for volume
func (state *StateValue) SetRange(min, max interface{}) error {
func (state *StateVariable) SetRange(min, max interface{}) error {
if min == nil || max == nil {
return fmt.Errorf("min and max must not be nil")
}
@@ -186,7 +186,7 @@ func (state *StateValue) SetRange(min, max interface{}) error {
// Returns:
//
// error: If no range exists or value can't be cast
func (state *StateValue) UpdateMinimalValue(value interface{}) error {
func (state *StateVariable) UpdateMinimalValue(value interface{}) error {
if state.valueRange == nil {
return fmt.Errorf("no range set for value %v", state.name)
}
@@ -210,7 +210,7 @@ func (state *StateValue) UpdateMinimalValue(value interface{}) error {
// Returns:
//
// error: If no range exists or value can't be cast
func (state *StateValue) UpdateMaximalValue(value interface{}) error {
func (state *StateVariable) UpdateMaximalValue(value interface{}) error {
if state.valueRange == nil {
return fmt.Errorf("no range set for value %v", state.name)
}
@@ -234,35 +234,35 @@ func (state *StateValue) UpdateMaximalValue(value interface{}) error {
// Returns:
//
// bool: True if within range or no range defined
func (state *StateValue) IsValueInRange(value interface{}) (bool, error) {
func (state *StateVariable) IsValueInRange(value interface{}) (bool, error) {
return state.valueType.InRange(value, state.valueRange)
}
// IsSendingEvents indicates if state changes trigger UPnP events.
func (state *StateValue) IsSendingEvents() bool {
func (state *StateVariable) IsSendingEvents() bool {
return state.sendEvents
}
// SetSendingEvents enables event notifications for state changes.
func (state *StateValue) SetSendingEvents() {
func (state *StateVariable) SetSendingEvents() {
state.sendEvents = true
log.Debugf("🐞 Enabling event sending for %s", state.name)
}
// UnsetSendingEvents disables event notifications for state changes.
func (state *StateValue) UnsetSendingEvents() {
func (state *StateVariable) UnsetSendingEvents() {
state.sendEvents = false
log.Debugf("🐞 Disabling event sending for %s", state.name)
}
// HasAllowedValues indicates if an allowed value list is defined.
func (state *StateValue) HasAllowedValues() bool {
func (state *StateVariable) HasAllowedValues() bool {
return len(state.allowedValues) > 0
}
// AllowedValues returns the list of permitted values.
// Returns an empty slice if no values are defined.
func (state *StateValue) AllowedValues() []interface{} {
func (state *StateVariable) AllowedValues() []interface{} {
return state.allowedValues
}
@@ -280,7 +280,7 @@ func (state *StateValue) AllowedValues() []interface{} {
// Example:
//
// err := state.AppendAllowedValue("PLAYING", "PAUSED", "STOPPED")
func (state *StateValue) AppendAllowedValue(value ...interface{}) error {
func (state *StateVariable) AppendAllowedValue(value ...interface{}) error {
state.allowedValues = slices.Grow(state.allowedValues, len(value))
for _, v := range value {
cv, err := state.valueType.Cast(v)
@@ -304,7 +304,7 @@ func (state *StateValue) AppendAllowedValue(value ...interface{}) error {
// Returns:
//
// bool: True if value is permitted or no list defined
func (state *StateValue) IsValueAllowed(value interface{}) (bool, error) {
func (state *StateVariable) IsValueAllowed(value interface{}) (bool, error) {
if !state.HasAllowedValues() {
return true, nil // No list = any value valid
}
@@ -331,7 +331,7 @@ func (state *StateValue) IsValueAllowed(value interface{}) (bool, error) {
// Returns:
//
// bool: True if value passes all applicable constraints
func (state *StateValue) IsValidValue(value interface{}) (bool, error) {
func (state *StateVariable) IsValidValue(value interface{}) (bool, error) {
cvalue, err := state.valueType.Cast(value)
if err != nil {
return false, err
@@ -349,31 +349,31 @@ func (state *StateValue) IsValidValue(value interface{}) (bool, error) {
return inrange && allowed, err
}
func (state *StateValue) HasDescription() bool {
func (state *StateVariable) HasDescription() bool {
return len(state.description) > 0
}
func (state *StateValue) Description() string {
func (state *StateVariable) Description() string {
return state.description
}
func (state *StateValue) SetDescription(desc string) {
func (state *StateVariable) SetDescription(desc string) {
state.description = strings.TrimSpace(desc)
}
func (state *StateValue) IsConstant() bool {
func (state *StateVariable) IsConstant() bool {
return !state.modifiable
}
func (state *StateValue) SetConstant() {
func (state *StateVariable) SetConstant() {
state.modifiable = false
}
func (state *StateValue) SetModifiable() {
func (state *StateVariable) SetModifiable() {
state.modifiable = true
}
func (state *StateValue) SetStep(step interface{}) error {
func (state *StateVariable) SetStep(step interface{}) error {
// Validation que le step correspond au type de la variable
if _, err := state.valueType.Cast(step); err != nil {
return fmt.Errorf("invalid step type: %v", err)
@@ -382,20 +382,43 @@ func (state *StateValue) SetStep(step interface{}) error {
return nil
}
func (state *StateValue) UnsetStep() {
func (state *StateVariable) UnsetStep() {
state.step = nil
}
func (state *StateValue) HasStep() bool {
func (state *StateVariable) HasStep() bool {
return state.step != nil
}
func (state *StateValue) Step() interface{} {
func (state *StateVariable) Step() interface{} {
return state.step
}
func (state *StateValue) NewInstance() *StateValueInstance {
return &StateValueInstance{
func (state *StateVariable) SetAllowedValues(allowed ...interface{}) {
state.ClearAllowedValues()
state.AppendAllowedValues(allowed...)
}
func (state *StateVariable) AppendAllowedValues(allowed ...interface{}) error {
state.allowedValues = slices.Grow(state.allowedValues, len(allowed))
var err error
for _, val := range allowed {
val, err = state.valueType.Cast(val)
if err != nil {
return err
}
state.allowedValues = append(state.allowedValues, val)
}
return nil
}
func (state *StateVariable) ClearAllowedValues() {
state.allowedValues = make([]interface{}, 0)
}
func (state *StateVariable) NewInstance() *StateVarInstance {
return &StateVarInstance{
model: state,
value: state.DefaultValue(),
lastChange: time.Now(),
@@ -405,7 +428,7 @@ func (state *StateValue) NewInstance() *StateValueInstance {
// ToXMLElement generates the complete XML representation of the state variable
// Returns an etree.Element that can be serialized to XML
func (sv *StateValue) ToXMLElement() *etree.Element {
func (sv *StateVariable) ToXMLElement() *etree.Element {
// Create root <stateVariable> element
elem := etree.NewElement("stateVariable")
elem.CreateAttr("name", sv.name)
@@ -468,7 +491,7 @@ func (sv *StateValue) ToXMLElement() *etree.Element {
// valueToString converts a value to its UPnP-compatible string representation
// Handles type-specific formatting for proper XML serialization
func (sv *StateValue) valueToString(val interface{}) string {
func (sv *StateVariable) valueToString(val interface{}) string {
if val == nil {
return "" // Safeguard against nil values
}

View File

@@ -0,0 +1,17 @@
package stateVariables
var TransferStatus = func() *StateVariable {
ts := StateType_String.NewStateValue("TransferStatus")
ts.SetAllowedValues(
"COMPLETED",
"ERROR",
"IN_PROGRESS",
"NONE",
)
ts.SetSendingEvents()
return ts
}()

View File

@@ -0,0 +1,13 @@
package stateVariables
var Volume = func() *StateVariable {
vol := StateType_UI2.NewStateValue("Volume")
vol.SetRange(0, 100)
vol.SetStep(1)
vol.SetSendingEvents()
return vol
}()

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
// Add performs addition operation on the given parameters 'a' and 'b'. It calls the
// corresponding method from valueType which is assumed to be an interface that
@@ -15,25 +15,25 @@ package upnp
// } else {
// // Use result
// }
func (sv StateValue) Add(a, b interface{}) (interface{}, error) {
func (sv StateVariable) Add(a, b interface{}) (interface{}, error) {
return sv.valueType.Add(a, b)
}
// Sub performs subtraction operation on the given parameters 'a' and 'b'. It follows
// similar semantics as in the Add method, but for subtraction instead of addition.
func (sv StateValue) Sub(a, b interface{}) (interface{}, error) {
func (sv StateVariable) Sub(a, b interface{}) (interface{}, error) {
return sv.valueType.Sub(a, b)
}
// Mul performs multiplication operation on the given parameters 'a' and 'b'. It follows
// similar semantics as in the Add method, but for multiplication instead of addition.
func (sv StateValue) Mul(a, b interface{}) (interface{}, error) {
func (sv StateVariable) Mul(a, b interface{}) (interface{}, error) {
return sv.valueType.Mul(a, b)
}
// Div performs division operation on the given parameters 'a' and 'b'. It follows similar
// semantics as in the Add method, but for division instead of addition. Please note that
// division by zero is undefined and will result in an error being returned from valueType.Div call.
func (sv StateValue) Div(a, b interface{}) (interface{}, error) {
func (sv StateVariable) Div(a, b interface{}) (interface{}, error) {
return sv.valueType.Div(a, b)
}

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
// Cast transforms any given value into an interface suitable for use in a StateValue object, using
// the underlying type's specific casting rules. It will return an error if the transformation fails or
@@ -24,6 +24,6 @@ package upnp
// log.Println(err)
// return
// }
func (sv *StateValue) Cast(val interface{}) (interface{}, error) {
func (sv *StateVariable) Cast(val interface{}) (interface{}, error) {
return sv.valueType.Cast(val)
}

View File

@@ -0,0 +1,13 @@
package stateVariables
func (sv StateVariable) Cmp(a, b interface{}) (int, error) {
return sv.valueType.Cmp(a, b)
}
func (sv StateVariable) Equal(a, b interface{}) (int, error) {
return sv.valueType.Cmp(a, b)
}
func (sv StateVariable) InRange(val interface{}, interval *ValueRange) (bool, error) {
return sv.valueType.InRange(val, interval)
}

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
// IsNumeric checks whether a given StateValue model represents a numeric value or
// not. Numeric types are defined as those that can be used to store number-like
@@ -9,7 +9,7 @@ package upnp
//
// Returns true if the given StateValue represents a numeric value; false
// otherwise.
func (sv StateValue) IsNumeric() bool {
func (sv StateVariable) IsNumeric() bool {
return sv.valueType.IsNumeric()
}
@@ -20,7 +20,7 @@ func (sv StateValue) IsNumeric() bool {
//
// Returns: bool: If the state variable type is any of the defined integer types
// (UI1, UI2, UI4, I1, I2, I4, Int), it returns true. Otherwise, it returns false.
func (sv StateValue) IsInteger() bool {
func (sv StateVariable) IsInteger() bool {
return sv.valueType.IsInteger()
}
@@ -28,20 +28,20 @@ func (sv StateValue) IsInteger() bool {
//
// The return value will be a boolean indicating whether the state value type is
// a signed integer (true) or not (false).
func (sv StateValue) IsSignedInt() bool {
func (sv StateVariable) IsSignedInt() bool {
return sv.valueType.IsSignedInt()
}
// IsUnsignedInt checks if the state value type represents an unsigned integer.
// The method returns a boolean indicating whether the state value type is an
// unsigned integer.
func (sv StateValue) IsUnsignedInt() bool {
func (sv StateVariable) IsUnsignedInt() bool {
return sv.valueType.IsUnsignedInt()
}
// IsFloat returns true if the StateValue's value type represents a floating point
// number; false otherwise.
func (sv StateValue) IsFloat() bool {
func (sv StateVariable) IsFloat() bool {
return sv.valueType.IsFloat()
}
@@ -54,7 +54,7 @@ func (sv StateValue) IsFloat() bool {
// Returns:
//
// (bool) : Indicates whether the StateValue is of boolean type or not.
func (sv StateValue) IsBool() bool {
func (sv StateVariable) IsBool() bool {
return sv.valueType.IsBool()
}
@@ -82,7 +82,7 @@ func (sv StateValue) IsBool() bool {
//
// state := upnp.StateValue{valueType: upnp.TypeInt}
// fmt.Println(state.IsString()) // Outputs: false
func (sv StateValue) IsString() bool {
func (sv StateVariable) IsString() bool {
return sv.valueType.IsString()
}
@@ -109,7 +109,7 @@ func (sv StateValue) IsString() bool {
// fmt.Println(state.IsTime())
//
// Outputs: false
func (sv StateValue) IsTime() bool {
func (sv StateVariable) IsTime() bool {
return sv.valueType.IsTime()
}
@@ -120,7 +120,7 @@ func (sv StateValue) IsTime() bool {
// UUID; false otherwise.
//
// Returns: bool: Indicates whether the StateValue is of UUID type or not.
func (sv StateValue) IsUUID() bool {
func (sv StateVariable) IsUUID() bool {
return sv.valueType.IsUUID()
}
@@ -129,7 +129,7 @@ func (sv StateValue) IsUUID() bool {
// a Uniform Resource Identifier (URI) or not.
//
// Returns: bool: Indicates whether the StateValue is of URI type or not.
func (sv StateValue) IsURI() bool {
func (sv StateVariable) IsURI() bool {
return sv.valueType.IsURI()
}
@@ -141,7 +141,7 @@ func (sv StateValue) IsURI() bool {
// Returns: bool: If the underlying value type of the StateValue object is
// either TypeBinBase64 or TypeBinHex, this method will return true; otherwise,
// it returns false.
func (sv StateValue) IsBinary() bool {
func (sv StateVariable) IsBinary() bool {
return sv.valueType.IsBinary()
}
@@ -153,6 +153,6 @@ func (sv StateValue) IsBinary() bool {
// //
// Returns: bool: A boolean value indicating whether the given StateValue is
// comparable or not. True means it's comparable, False means it isn't.
func (sv StateValue) IsComparable() bool {
func (sv StateVariable) IsComparable() bool {
return sv.valueType.IsComparable()
}

View File

@@ -1,7 +1,7 @@
// Package upnp provides comprehensive handling of UPnP state variable types.
// package stateVariables provides comprehensive handling of UPnP state variable types.
// It includes type identification, value casting, comparison, and range validation
// for all standard UPnP state variable types.
package upnp
package stateVariables
import (
"strings"
@@ -143,8 +143,8 @@ func (t StateVarType) BitSize() int {
// empty map for event conditions. If name is an empty string, it will cause panic in later
// usage. Name is typically used to identify or represent a specific value or condition
// associated with the variable type 't'.
func (t StateVarType) NewStateValue(name string) *StateValue {
return &StateValue{
func (t StateVarType) NewStateValue(name string) *StateVariable {
return &StateVariable{
name: name,
valueType: t,
eventConditions: make(map[string]StateConditionFunc),

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
// Add performs addition operation on two interfaces if both are of numeric
// type, otherwise it returns an error. If the types are not numeric, it checks

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"bytes"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
// IsNumeric checks whether a given StateVarType represents a numeric type or
// not. Numeric types are defined as those that can be used to store number-like

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"encoding/base64"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"errors"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"errors"
@@ -127,7 +127,7 @@ func cmpInt(a, b int64) int {
}
}
// cmpUint compares two unsigned integers, a and b.
// cmpUint compares two unsigned integers, a and b.
// It returns -1 if a < b.
// It returns 1 if a > b.
// It returns 0 if a == b.

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import "fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"errors"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package upnp
package stateVariables
import "fmt"