Skip to content

Commit

Permalink
Added initial DataSet implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchiecarroll committed Sep 24, 2021
1 parent cd27ff7 commit c157eda
Show file tree
Hide file tree
Showing 10 changed files with 901 additions and 37 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"cSpell.words": [
"ALOG",
"ALRM",
"ANTLR",
"bufio",
"DFDT",
"Gbtc",
"goapi",
"GPLAINS",
"IDXOR",
"IPHA",
"IPHM",
"msdata",
"phasor",
"Ritchie",
"strconv",
Expand Down
154 changes: 154 additions & 0 deletions examples/AdvancedSubscribe/AdvancedSubscribe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//******************************************************************************************************
// AdvancedSubscribe.go - Gbtc
//
// Copyright © 2021, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 09/23/2021 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************

package main

import (
"bufio"
"fmt"
"math"
"os"
"strconv"
"strings"
"time"

"github.com/sttp/goapi/sttp"
"github.com/sttp/goapi/sttp/transport"
)

// AdvancedSubscriber is a simple STTP data subscriber implementation.
type AdvancedSubscriber struct {
sttp.SubscriberBase // Provides default implementation
}

// NewAdvancedSubscriber creates a new AdvancedSubscriber.
func NewAdvancedSubscriber() *AdvancedSubscriber {
subscriber := &AdvancedSubscriber{}
subscriber.SubscriberBase = sttp.NewSubscriberBase(subscriber)
return subscriber
}

func main() {
hostname, port := parseCmdLineArgs()
subscriber := NewAdvancedSubscriber()
subscription := subscriber.Subscription()

subscriber.Hostname = hostname
subscriber.Port = port

subscription.FilterExpression = "FILTER TOP 20 ActiveMeasurements WHERE True"
subscription.UdpDataChannel = true
subscription.DataChannelLocalPort = 9600
subscription.UseMillisecondResolution = true

subscriber.Connect()
defer subscriber.Dispose()

reader := bufio.NewReader(os.Stdin)
reader.ReadRune()
}

// ReceivedMetadata handles reception of the metadata response.
func (ss *AdvancedSubscriber) ReceivedMetadata(metadata []byte) {
ss.StatusMessage(fmt.Sprintf("Received %d bytes of metadata", len(metadata)))
}

// SubscriptionUpdated handles notifications that a new SignalIndexCache has been received.
func (ss *AdvancedSubscriber) SubscriptionUpdated(signalIndexCache *transport.SignalIndexCache) {
ss.StatusMessage(fmt.Sprintf("Received signal index cache with %d mappings", signalIndexCache.Count()))
}

var lastMessageDisplay time.Time

// ReceivedNewMeasurements handles reception of new measurements.
func (ss *AdvancedSubscriber) ReceivedNewMeasurements(measurements []transport.Measurement) {

if time.Since(lastMessageDisplay).Seconds() < 5.0 {
return
}

defer func() { lastMessageDisplay = time.Now() }()

if lastMessageDisplay.IsZero() {
ss.StatusMessage("Receiving measurements...")
return
}

var message strings.Builder

message.WriteString(strconv.FormatUint(ss.TotalMeasurementsReceived(), 10))
message.WriteString(" measurements received so far...\n")
message.WriteString("Timestamp: ")
message.WriteString(measurements[0].DateTime().Format("2006-01-02 15:04:05.999999999"))
message.WriteRune('\n')
message.WriteString("\tID\tSignal ID\t\t\t\tValue\n")

for i := 0; i < len(measurements); i++ {
measurement := measurements[i]
metadata := ss.Metadata(&measurement)

message.WriteRune('\t')
message.WriteString(strconv.FormatUint(metadata.ID, 10))
message.WriteRune('\t')
message.WriteString(measurement.SignalID.String())
message.WriteRune('\t')
message.WriteString(strconv.FormatFloat(measurement.Value, 'f', 6, 64))
message.WriteRune('\n')
}

ss.StatusMessage(message.String())
}

// ConnectionTerminated handles notification that a connection has been terminated.
func (ss *AdvancedSubscriber) ConnectionTerminated() {
// Call base implementation which will display a connection terminated message to stderr
ss.SubscriberBase.ConnectionTerminated()

// Reset last message display time on disconnect
lastMessageDisplay = time.Time{}
}

func parseCmdLineArgs() (string, uint16) {
args := os.Args

if len(args) < 3 {
fmt.Println("Usage:")
fmt.Println(" AdvancedSubscribe HOSTNAME PORT")
os.Exit(1)
}

hostname := args[1]
port, err := strconv.Atoi(args[2])

if err != nil {
fmt.Printf("Invalid port number \"%s\": %s\n", args[1], err.Error())
os.Exit(2)
}

if port < 1 || port > math.MaxUint16 {
fmt.Printf("Port number \"%s\" is out of range: must be 1 to %d\n", args[1], math.MaxUint16)
os.Exit(2)
}

return hostname, uint16(port)
}
2 changes: 1 addition & 1 deletion sttp/[TypeName]HashSet.tt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var member void
// TODO: Code can be changed from template to type "T" when generics are available:
//type HashSet[T any] map[T]void

// <#=TypeName#>HashSet represents a distinct collection of values, i.e., a set.
// <#=TypeName#>HashSet represents a distinct collection of <#=TypeName#> values, i.e., a set.
// A <#=TypeName#>HashSet is not sorted and will not contain duplicate elements.
// The methods of the <#=TypeName#>HashSet are not intrinsically thread-safe procedures,
// to guarantee thread safety, you should initiate a lock before calling a method.
Expand Down
2 changes: 1 addition & 1 deletion sttp/guid/HashSet.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var member void
// TODO: Code can be changed from template to type "T" when generics are available:
//type HashSet[T any] map[T]void

// HashSet represents a distinct collection of values, i.e., a set.
// HashSet represents a distinct collection of Guid values, i.e., a set.
// A HashSet is not sorted and will not contain duplicate elements.
// The methods of the HashSet are not intrinsically thread-safe procedures,
// to guarantee thread safety, you should initiate a lock before calling a method.
Expand Down
78 changes: 78 additions & 0 deletions sttp/metadata/DataColumn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//******************************************************************************************************
// DataColumn.go - Gbtc
//
// Copyright © 2021, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 09/23/2021 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************

package metadata

// DataColumn represents a column, i.e., a field, in a DataTable defining a name and a data type.
// Data columns can also be computed where its value would be derived from other columns and
// functions (https://sttp.github.io/documentation/filter-expressions/) defined in an expression.
type DataColumn struct {
parent *DataTable
name string
dataType DataTypeEnum
expression string
computed bool
index int
}

func newDataColumn(parent *DataTable, name string, dataType DataTypeEnum, expression string) *DataColumn {
return &DataColumn{
parent: parent,
name: name,
dataType: dataType,
expression: expression,
computed: len(expression) > 0,
index: -1,
}
}

// Parent gets the parent DataTable of the DataColumn.
func (dc *DataColumn) Parent() *DataTable {
return dc.parent
}

// Name gets the column name of the DataColumn.
func (dc *DataColumn) Name() string {
return dc.name
}

// Type gets the column DataType enumeration value of the DataColumn.
func (dc *DataColumn) Type() DataTypeEnum {
return dc.dataType
}

// Expression gets the column expression value of the DataColumn, if any.
func (dc *DataColumn) Expression() string {
return dc.expression
}

// Computed gets a flag that determines if the DataColumn is a computed value,
// i.e., has a defined expression.
func (dc *DataColumn) Computed() bool {
return dc.computed
}

// Index gets the index of the DataColumn within its parent DataTable columns collection.
func (dc *DataColumn) Index() int {
return dc.index
}
Loading

0 comments on commit c157eda

Please sign in to comment.