Skip to content

Commit

Permalink
Overhauled how things were processed
Browse files Browse the repository at this point in the history
We now process every csv file in the Input directory for power data.
SampleData.csv moved to Input folder.

Split some functions out to separate files for ease of reading.
runCalculations now returns a struct of PowerDataReturn that we can use
to print output later.
WaitGroup was setup to prevent exiting before the go routines finish
collecting data from the files.
  • Loading branch information
HelixSpiral committed Mar 1, 2018
1 parent 8c0fad1 commit e0cc17c
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 74 deletions.
85 changes: 85 additions & 0 deletions Calculations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import "sync"

// Run the calculations on the file and return a struct of PowerDataReturn
func runCalculations(path string, data [][]PowerData, wg *sync.WaitGroup) PowerDataReturn {
var totalkWh float64
var totalDataPoints float64
var totalDailykWh float64
var totalDays float64
var lowestDay string
var highestDay string

lowestDaykWh, highestDaykWh := 100.00, 0.0 // We default lowestDay to 100 and highestDay to 0
dailykWh := make(map[string]float64) // Track daily
hourlykWh := make(map[string]float64) // Track hourly
highestHour := make(map[string]float64) // Track highest seen per hour
lowestHour := make(map[string]float64) // Track lowest seen per hour

// Loop for all the lines in data
for _, dayData := range data {
// Loop for each hour
for _, hourData := range dayData {
// Check to see if the map for that lowestHour exists, if not create it with the default value of 100
if _, ok := lowestHour[hourData.date.Format("03:04:05 PM")]; !ok {
lowestHour[hourData.date.Format("03:04:05 PM")] = 100
}

// Add the kWh usage to the totals
totalkWh += hourData.kWh
dailykWh[hourData.date.Format("01/02/2006")] += hourData.kWh

// Track the totals by hour as well
hourlykWh[hourData.date.Format("03:04:05 PM")] += hourData.kWh
if hourData.kWh > highestHour[hourData.date.Format("03:04:05 PM")] {
highestHour[hourData.date.Format("03:04:05 PM")] = hourData.kWh
}

if hourData.kWh < lowestHour[hourData.date.Format("03:04:05 PM")] && hourData.kWh > 0 {
lowestHour[hourData.date.Format("03:04:05 PM")] = hourData.kWh
}

// Increase the data point total
totalDataPoints += 1
}
}

// Loop for each day
for day, dailyusage := range dailykWh {
if dailyusage > highestDaykWh {
highestDaykWh = dailyusage
highestDay = day
}

if dailyusage < lowestDaykWh && dailyusage > 0 {
lowestDaykWh = dailyusage
lowestDay = day
}

// Add to the total daily usage
totalDailykWh += dailyusage

// Increase the number of days
totalDays += 1
}

wg.Done() // Tell the wg we're done.

// Return the struct
return PowerDataReturn{
filePath: path,
lowestDay: lowestDay,
highestDay: highestDay,
totalkWh: totalkWh,
averageDailykWh: totalDailykWh / totalDays,
averageHourlykWh: totalkWh / totalDataPoints,
lowestDailykWh: lowestDaykWh,
highestDailykWh: highestDaykWh,
totalDays: totalDays,
totalDataPoints: totalDataPoints,
hourlykWh: hourlykWh,
highestHour: highestHour,
lowestHour: lowestHour,
}
}
41 changes: 41 additions & 0 deletions FileFunctions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"fmt" // Used for printing error messages
"os" // Needed for os.FileInfo
"path/filepath" // Needed for filepath.Walk
"strings" // Needed for strings.Contains
)

// Get a list of the files we want to take input from
func returnInputFiles(inputFolder string) []string {
var inputFiles []string
err := filepath.Walk(inputFolder, func(path string, fileInfo os.FileInfo, err error) error {

// Check for any errors
if err != nil {
fmt.Println("Error accessing:", err)
return err
}

// Ignore any folder not our input folder - we're not doing recursive searching
if fileInfo.IsDir() && fileInfo.Name() != "Input" {
return filepath.SkipDir
}

// Make sure the file is a .csv
if strings.Contains(path, ".csv") {
inputFiles = append(inputFiles, path)
}

// Return no error if we made it this far
return nil
})

// Error handling for the above function
if err != nil {
fmt.Println("Error:", err)
}

return inputFiles
}
File renamed without changes.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Take historical kWh usage and return averages and other data
This is part of an on going project of researching the viability of getting solar panels

## Usage
Run in a directory with a file named PowerData.csv
Run in a directory with a folder named "Input". All files in the folder will be evaluated.

Power data should be a csv file in the following format:
Power data should be csv files in the following format:

````
1/19/2018 12:00:00 AM,0.18
Expand Down
128 changes: 56 additions & 72 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,91 +4,75 @@
package main

import (
"fmt"
"time"
"fmt" // Needed to print output
"sync" // Needed for WaitGroups
"time" // Needed for time.Time
)

// Populated from the csv files
type PowerData struct {
date time.Time
kWh float64
}

// Populated from Calculations.go
type PowerDataReturn struct {
filePath string // Path of the file
lowestDay string // Day the lowest kWh was on
highestDay string // Day the highest kWh was on

totalkWh float64 // Total kWh used
averageDailykWh float64 // Average daily kWh used
averageHourlykWh float64 // Average hourly kWh used
lowestDailykWh float64 // Lowest daily kWh found
highestDailykWh float64 // Highest daily kWh found
totalDays float64 // Total days in the file
totalDataPoints float64 // Total data points in the file

hourlykWh map[string]float64 // Hourly data
lowestHour map[string]float64 // Lowest kWh at each hour
highestHour map[string]float64 // Highest kWh at each hour
}

func main() {
// Setup some variables
var totalkWh float64
var totalDataPoints float64
var totalDailykWh float64
var totalDays float64
var lowestDay string
var highestDay string

lowestDaykWh, highestDaykWh := 100.00, 0.0 // We default lowestDay to 100 and highestDay to 0
dailykWh := make(map[string]float64) // Track daily
hourlykWh := make(map[string]float64) // Track hourly
highestHour := make(map[string]float64) // Track highest seen per hour
lowestHour := make(map[string]float64) // Track lowest seen per hour

// Read the data from the csv
data := readData("PowerData.csv")

// Loop for each day
for _, dayData := range data {
// Loop for each hour
for _, hourData := range dayData {
// Check to see if the map for that lowestHour exists, if not create it with the default value of 100
if _, ok := lowestHour[hourData.date.Format("03:04:05 PM")]; !ok {
lowestHour[hourData.date.Format("03:04:05 PM")] = 100
}

// Add the kWh usage to the totals
totalkWh += hourData.kWh
dailykWh[hourData.date.Format("01/02/2006")] += hourData.kWh

// Track the totals by hour as well
hourlykWh[hourData.date.Format("03:04:05 PM")] += hourData.kWh
if hourData.kWh > highestHour[hourData.date.Format("03:04:05 PM")] {
highestHour[hourData.date.Format("03:04:05 PM")] = hourData.kWh
}

if hourData.kWh < lowestHour[hourData.date.Format("03:04:05 PM")] && hourData.kWh > 0 {
lowestHour[hourData.date.Format("03:04:05 PM")] = hourData.kWh
}

// Increase the data point total
totalDataPoints += 1
}
}
var inputFiles []string // List of the input files we have
var wg sync.WaitGroup // Setup a waitgroup for the go routines

// Loop for each day
for day, dailyusage := range dailykWh {
if dailyusage > highestDaykWh {
highestDaykWh = dailyusage
highestDay = day
}
var outputInfo []PowerDataReturn

if dailyusage < lowestDaykWh && dailyusage > 0 {
lowestDaykWh = dailyusage
lowestDay = day
}
// Folder for input files
inputFolder := "D:\\GitHub\\PowerCalculations\\Input"

// Get all the files we want to take input from
inputFiles = returnInputFiles(inputFolder)

// Add to the total daily usage
totalDailykWh += dailyusage
// Loop for each input file we stored
for x := range inputFiles {
wg.Add(1) // Add one to the waitgroup

// Run this function inside a go routine so we can do it concurrently.
go func(x int) {
data := readData(inputFiles[x])
outputInfo = append(outputInfo, runCalculations(inputFiles[x], data, &wg))
}(x)

// Increase the number of days
totalDays += 1
}

// Print the data
fmt.Printf("Total kWh usage: %.03f\r\n", totalkWh)
fmt.Printf("Average hourly kWh: %.03f\r\n", totalkWh/totalDataPoints)
fmt.Printf("Average daily kWh: %.03f\r\n", totalDailykWh/totalDays)
fmt.Printf("Lowest daily kWh: %.03f on %s\r\n", lowestDaykWh, lowestDay)
fmt.Printf("Highest daily kWh: %.03f on %s\r\n", highestDaykWh, highestDay)
fmt.Println("Total days:", totalDays)
fmt.Println("Total data points:", totalDataPoints)

// Loop for each hour and print the average kWh usage for that hour
for x, y := range hourlykWh {
fmt.Printf("Hour: %v | Usage: %.03f, Highest: %.03f | Lowest: %.03f\r\n", x, y/totalDays, highestHour[x], lowestHour[x])
// Wait for the go routines to finish and then print
wg.Wait()

// Loop the slice of PowerDataReturns and provide output.
for x := range outputInfo {
fmt.Println("Power data for file:", outputInfo[x].filePath)
fmt.Printf("Total kWh usage: %.03f\r\n", outputInfo[x].totalkWh)
fmt.Printf("Average daily kWh: %.03f\r\n", outputInfo[x].averageDailykWh)
fmt.Printf("Average hourly kWh: %.03f\r\n", outputInfo[x].averageHourlykWh)
fmt.Printf("Lowest daily kWh: %.03f on %s\r\n", outputInfo[x].lowestDailykWh, outputInfo[x].lowestDay)
fmt.Printf("Highest daily kWh: %.03f on %s\r\n", outputInfo[x].highestDailykWh, outputInfo[x].highestDay)

// Loop for each hour and print hour-specific data.
for y, z := range outputInfo[x].hourlykWh {
fmt.Printf("Hour: %v | Usage: %.03f, Highest: %.03f | Lowest: %.03f\r\n", y, z/outputInfo[x].totalDays, outputInfo[x].highestHour[y], outputInfo[x].lowestHour[y])
}
}
}

0 comments on commit e0cc17c

Please sign in to comment.