package iSolarCloud import ( "GoSungrow/iSolarCloud/api" "GoSungrow/iSolarCloud/api/GoStruct/output" "GoSungrow/iSolarCloud/api/GoStruct/valueTypes" "encoding/json" "errors" "fmt" "github.com/MickMake/GoUnify/Only" "os" "sort" "strings" "time" ) // ****************************************************** // func (sg *SunGrow) NewSunGrowData() SunGrowData { var data SunGrowData for range Only.Once { data.New(sg) data.SetOutputType(sg.OutputType) data.SetSaveAsFile(sg.SaveAsFile) } return data } func SplitArg(arg string) []string { var ret []string for range Only.Once { ret = []string{arg} for _, s := range []string{ ",", "/", " "} { if strings.Contains(arg, s) { ret = strings.Split(arg, s) break } } } return ret } type EndPoints map[string]EndPoint type EndPoint struct { Func SunGrowDataFunction HasArgs bool } type SunGrowData struct { Args []string endPoints []string Request SunGrowDataRequest Results SunGrowDataResults sunGrow *SunGrow outputType output.OutputType saveAsFile bool cacheTimeout time.Duration Debug bool Error error } func (sgd *SunGrowData) PrintDebug(format string, args ...interface{}) { if sgd.Debug { _, _ = fmt.Fprintf(os.Stderr, format, args...) } } func (sgd *SunGrowData) New(ref *SunGrow) { for range Only.Once { sgd.sunGrow = ref sgd.Results = make(SunGrowDataResults) sgd.cacheTimeout = time.Minute * 5 } } func (sgd *SunGrowData) SetCacheTimeout(t time.Duration) { sgd.cacheTimeout = t } func (sgd *SunGrowData) SetOutput(t string) { sgd.outputType.Set(t) } func (sgd *SunGrowData) SetOutputType(t output.OutputType) { sgd.outputType = t } func (sgd *SunGrowData) SetSaveAsFile(yes bool) { sgd.saveAsFile = yes } func (sgd *SunGrowData) SetEndpoints(endpoints ...string) { sgd.endPoints = endpoints } func (sgd *SunGrowData) SetArgs(args ...string) { sgd.Args = args } func (sgd *SunGrowData) SetPsIds(psids ...string) { for range Only.Once { var pids valueTypes.PsIds pids = sgd.sunGrow.SetPsIds(psids...) if sgd.Error != nil { break } sgd.Request.SetPSIDs(pids.Strings()) } } func (sgd *SunGrowData) CallEndpoint(endpoint api.EndPoint, request SunGrowDataRequest) SunGrowDataResponse { var response SunGrowDataResponse for range Only.Once { if !request.Validate(endpoint) { request.Help(endpoint) sgd.Error = errors.New("missing argument") break } var req []byte req, sgd.Error = json.Marshal(request) if sgd.Error != nil { fmt.Printf("GetEndPoint - Error: %s\n", sgd.Error) break } // fmt.Printf("Request: %s\n", req) if string(req) != "" { endpoint = endpoint.SetRequestByJson(output.Json(req)) sgd.Error = endpoint.GetError() if sgd.Error != nil { fmt.Println(endpoint.Help()) break } } sgd.PrintDebug("Request: %s\n", endpoint.GetRequestJson()) // @TODO - Make this a config option. endpoint = endpoint.SetCacheTimeout(sgd.cacheTimeout) endpoint = endpoint.Call() sgd.Error = endpoint.GetError() if sgd.Error != nil { if strings.Contains(sgd.Error.Error(), "er_token_login_invalid") { sgd.sunGrow.Logout() break } fmt.Println(endpoint.Help()) sgd.PrintDebug("Error response[%s]: %s\n", sgd.Error, endpoint.GetResponseJson()) break } sgd.PrintDebug("Response: %s\n", endpoint.GetResponseJson()) response.Data = endpoint.GetEndPointData() args := request.GetArgs(response.Data.EndPoint) name := endpoint.GetArea().String() + "." + endpoint.GetName().String() var title string var file string // + " - " + request.RequestAsFilePrefix(), key := request.GetPrimaryArg() if key != "" { title = key file = key } response.Options = OutputOptions { Name: name, OutputType: sgd.sunGrow.OutputType, PrimaryKey: key, FileSuffix: file, SaveAsFile: sgd.sunGrow.SaveAsFile, TitleSuffix: args, GraphRequest: output.GraphRequest { Title: title, SubTitle: args, TimeColumn: nil, DataColumn: nil, UnitsColumn: nil, NameColumn: nil, DataMin: nil, DataMax: nil, Width: nil, Height: nil, Error: nil, }, } sgd.PrintDebug("OutputOptions: %v\n", response.Options) } return response } func (sgd *SunGrowData) GetData() error { for range Only.Once { if len(sgd.endPoints) == 0 { sgd.Error = errors.New("need an endpoint") break } for _, endpoint := range sgd.endPoints { sgd.Error = sgd.GetDataSingle(endpoint) if sgd.Error != nil { break } } } return sgd.Error } func (sgd *SunGrowData) GetDataSingle(endpoint string) error { for range Only.Once { // Lookup endpoint interface from string. ep := sgd.sunGrow.GetEndpoint(endpoint) if sgd.sunGrow.IsError() { sgd.Error = sgd.sunGrow.Error break } sgd.Request.SetRequired(ep.GetRequestArgNames()) sgd.Request.SetArgs(sgd.Args...) // PsId not required. if sgd.Request.IsPsIdNotRequired() { sgd.Error = sgd.getDataSinglePsIdNotRequired(ep) break } // PsId required. if sgd.Request.IsPsIdRequired() { sgd.Error = sgd.getDataSinglePsIdRequired(ep) break } } return sgd.Error } func (sgd *SunGrowData) getDataSinglePsIdNotRequired(ep api.EndPoint) error { for range Only.Once { var result SunGrowDataResult result.EndPointArea = ep.GetArea() result.EndPointName = ep.GetName() result.EndPoint = ep result.Request = sgd.Request result.Response = sgd.CallEndpoint(ep, result.Request) if sgd.Error != nil { break } sgd.Results[result.EndPointName.String()] = result sgd.Error = sgd.Process() if sgd.Error != nil { break } } return sgd.Error } func (sgd *SunGrowData) getDataSinglePsIdRequired(ep api.EndPoint) error { for range Only.Once { if sgd.Request.aPsId == nil { sgd.SetPsIds() } for _, psId := range sgd.Request.aPsId { var result SunGrowDataResult result.Request = sgd.Request result.Request.SetPsId(psId.String()) result.EndPointArea = ep.GetArea() result.EndPointName = ep.GetName() result.EndPoint = ep result.Response = sgd.CallEndpoint(ep, result.Request) if sgd.Error != nil { break } sgd.Results[result.EndPointName.String() + "/" + psId.String()] = result } if sgd.Error != nil { break } sgd.Error = sgd.Process() if sgd.Error != nil { break } } return sgd.Error } func (sgd *SunGrowData) Process() error { for range Only.Once { if len(sgd.Results) == 0 { fmt.Println("No results found.") break } for _, result := range sgd.Results { result.Response.Data.ProcessMap() if sgd.Error != nil { break } } } return sgd.Error } func (sgd *SunGrowData) Output() error { for range Only.Once { if len(sgd.Results) == 0 { fmt.Println("No results found.") break } for _, result := range sgd.Results { sgd.Error = result.Response.Output() if sgd.Error != nil { break } } } return sgd.Error } func (sgd *SunGrowData) OutputDataTables() error { for range Only.Once { if len(sgd.Results) == 0 { fmt.Println("No results found.") break } for _, result := range sgd.Results { sgd.Error = result.Response.OutputDataTables() if sgd.Error != nil { break } } } return sgd.Error } type SunGrowDataResults map[string]SunGrowDataResult type SunGrowDataResult struct { EndPointArea api.AreaName EndPointName api.EndPointName EndPoint api.EndPoint Request SunGrowDataRequest Response SunGrowDataResponse Error error } func (sgd *SunGrowDataResult) Process() error { sgd.Response.Data.ProcessMap() sgd.Error = sgd.Response.Data.Error return sgd.Error } // func (sgd *SunGrowDataResult) ProcessMapForMqtt() error { // sgd.Response.Data.ProcessMapForMqtt() // sgd.Error = sgd.Response.Data.Error // return sgd.Error // } // func (sgd *SunGrowDataResult) CreateResultTable(full bool) output.Table { // ret := sgd.Response.CreateResultTable(full) // sgd.Error = sgd.Response.Data.Error // return ret // } // func (sgd *SunGrowDataResult) CreateDataTables() api.Tables { // tables := sgd.Response.CreateDataTables() // sgd.Error = sgd.Response.Data.Error // return tables // } func (sgd *SunGrowDataResult) Sort() []string { return sgd.Response.Data.Sort() } func (sgd *SunGrowDataResult) Print() { fmt.Println(sgd.Response.Data.String()) } type OutputOptions struct { Name string TitleSuffix string OutputType output.OutputType PrimaryKey string FileSuffix string SaveAsFile bool GraphRequest output.GraphRequest // table.InitGraph(output.GraphRequest { // Title: "", // TimeColumn: output.SetString("Date/Time"), // SearchColumn: output.SetString("Point Id"), // NameColumn: output.SetString("Point Name"), // ValueColumn: output.SetString("Value"), // UnitsColumn: output.SetString("Units"), // SearchString: output.SetString(""), // MinLeftAxis: output.SetFloat(0), // MaxLeftAxis: output.SetFloat(0), // }) } type SunGrowDataResponses map[string]SunGrowDataResponse type SunGrowDataFunction func(request SunGrowDataRequest) SunGrowDataResponse type SunGrowDataResponse struct { Data api.DataMap Options OutputOptions Error error } // func (sgd *SunGrowDataResponse) CreateResultTable(full bool) output.Table { // ret := sgd.Data.CreateResultTable(full) // sgd.Error = sgd.Data.Error // return ret // } // func (sgd *SunGrowDataResponse) CreateDataTables() api.Tables { // tables := sgd.Data.CreateDataTables() // sgd.Error = sgd.Data.Error // return tables // } func (sgd *SunGrowDataResponse) Output() error { for range Only.Once { // Outputs that don't drop through. if sgd.Options.OutputType.IsStruct() || sgd.Options.OutputType.IsList() || sgd.Options.OutputType.IsRaw() || sgd.Options.OutputType.IsJson() { table := sgd.Data.CreateResultTable(true) table.OutputType = sgd.Options.OutputType table.SetSaveFile(sgd.Options.SaveAsFile) table.AppendTitle(" - %s", sgd.Options.TitleSuffix) table.AppendFilePrefix(sgd.Options.FileSuffix) sgd.Error = table.Output() break } // Outputs that can drop through to DataTables. if sgd.Options.OutputType.IsTable() || sgd.Options.OutputType.IsXLSX() || sgd.Options.OutputType.IsCsv() || sgd.Options.OutputType.IsXML() { table := sgd.Data.CreateResultTable(false) table.OutputType = sgd.Options.OutputType table.SetSaveFile(sgd.Options.SaveAsFile) table.AppendTitle(" - %s", sgd.Options.TitleSuffix) table.AppendFilePrefix(sgd.Options.FileSuffix) sgd.Error = table.Output() if sgd.Error != nil { break } // break } sgd.Error = sgd.OutputDataTables() } return sgd.Error } func (sgd *SunGrowDataResponse) OutputDataTables() error { for range Only.Once { tables := sgd.Data.CreateDataTables() if sgd.Data.Error != nil { sgd.Error = sgd.Data.Error break } if len(tables) == 0 { break } // @iSolarCloud/api/struct_data.go:420 for _, data := range tables { if sgd.Options.TitleSuffix == "" { sgd.Options.TitleSuffix = data.Table.GetTitle() } data.Table.OutputType = sgd.Options.OutputType data.Table.SetSaveFile(sgd.Options.SaveAsFile) // sgd.Options.SaveAsFile if sgd.Options.OutputType.IsGraph() { if !data.IsValid { fmt.Printf("# %s.%s - has no graphable data.\n", data.Area, data.Name) continue } data.Table.SetSaveFile(true) if sgd.Options.GraphRequest.TimeColumn == nil { for _, col := range data.Table.GetHeaders() { val := data.Values.GetCell(0, col) if val.IsTypeDateTime() { sgd.Options.GraphRequest.TimeColumn = &col break } } } if sgd.Options.GraphRequest.TimeColumn == nil { // No time column - abort. break } if sgd.Options.GraphRequest.UnitsColumn != nil { for _, col := range data.Table.GetHeaders() { if *sgd.Options.GraphRequest.UnitsColumn != col { continue } val := data.Values.GetCell(0, col) unit := val.Unit() if unit != "" { continue } sgd.Options.GraphRequest.UnitsColumn = &col sgd.Options.GraphRequest.DataUnit = &unit break } } if sgd.Options.GraphRequest.NameColumn == nil { } // if sgd.Options.GraphRequest.Width == nil { // } // if sgd.Options.GraphRequest.Height == nil { // } var values []string if sgd.Options.GraphRequest.DataColumn == nil { fmt.Println("Finding points to graph...") fmt.Printf("Table Headers: %s\n", strings.Join(data.Table.GetHeaders(), ", ")) fmt.Printf("Table rows: %d\n", data.Rows) // We don't have any DataColumn defined - find them. for _, col := range data.Table.GetHeaders() { val := data.Values.GetCell(0, col) if !val.IsNumber() { continue } values = append(values, col) // if val.ValueKey() == "" { // values = append(values, col) // continue // } // if val.DeviceId() == "" { // values = append(values, val.ValueKey()) // continue // } // values = append(values, val.DeviceId() + "." + val.ValueKey()) } fmt.Printf("Found %d points.\n", len(values)) } title := data.Table.GetTitle() file := data.Table.GetFilePrefix() sort.Strings(values) for _, value := range values { // @TODO - Lookup pointIds here. sgd.Options.GraphRequest.DataColumn = &value if sgd.Options.PrimaryKey != "" { data.Table.SetTitle("%s - %s - %s", title, sgd.Options.PrimaryKey, value) data.Table.SetFilePrefix("%s-%s-%s", file, sgd.Options.PrimaryKey, value) } else { data.Table.SetTitle("%s - %s", title, value) data.Table.SetFilePrefix("%s-%s", file, value) } sgd.Options.GraphRequest.Title = data.Table.GetTitle() sgd.Error = data.Table.SetGraph(sgd.Options.GraphRequest) if sgd.Error != nil { break } sgd.Error = data.Table.Output() if sgd.Error != nil { break } } break } data.Table.AppendTitle(" - %s", sgd.Options.TitleSuffix) data.Table.AppendFilePrefix(sgd.Options.FileSuffix) fmt.Println() sgd.Error = data.Table.Output() if sgd.Error != nil { break } } } return sgd.Error } func (sgd *SunGrowDataResponse) LookUpPointId() { } func (sgd *SunGrowDataResponse) Print() { fmt.Println(sgd.Data.String()) }