Skip to content
Snippets Groups Projects
Select Git revision
  • d810c62493fcbeb0b12df7037be4fa27755db523
  • main default protected
  • master
3 results

_variables.scss

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    overview.go 10.43 KiB
    package handlers
    
    import (
    	"EgressAPI/other"
    	"encoding/json"
    	"log"
    	"net/http"
    	"strconv"
    	"time"
    )
    
    func OverviewHandler(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
    	w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL)
    	w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
    	if r.Method == http.MethodPost {
    
    		token, err := other.ValidateRequest(r.Body)
    		if err != nil {
    			w.WriteHeader(http.StatusUnauthorized)
    			return
    		}
    		overviewData, err := other.GetMachineDetailsForUser(token)
    		if err != nil {
    			// Log the error or handle it appropriately
    			log.Printf("Error getting machine details: %v", err)
    			w.WriteHeader(http.StatusInternalServerError)
    			return
    		}
    		//sort the data into intervals, and since day x
    		params := r.URL.Query()
    
    		since, err := strconv.ParseInt(params.Get("since"), 10, 64)
    		if err != nil {
    			log.Println(err.Error())
    			w.WriteHeader(http.StatusBadRequest)
    			return
    		}
    
    		interval, err := strconv.ParseInt(params.Get("interval"), 10, 64)
    		if err != nil {
    			log.Println(err.Error())
    			w.WriteHeader(http.StatusBadRequest)
    			return
    		}
    
    		start, err := strconv.ParseInt(params.Get("start"), 10, 64)
    		if err != nil {
    			log.Println(err.Error())
    			w.WriteHeader(http.StatusBadRequest)
    			return
    		}
    
    		overviewData, err = sortData(overviewData, interval, since, start)
    		datasets := setIntoDatasets(overviewData)
    		if err != nil {
    			log.Println(err.Error())
    			w.WriteHeader(http.StatusInternalServerError)
    			return
    		}
    		// Encode and write JSON response
    		err = json.NewEncoder(w).Encode(datasets)
    		if err != nil {
    			// Log the error or handle it appropriately
    			log.Printf("Error encoding JSON: %v", err)
    			w.WriteHeader(http.StatusInternalServerError)
    			return
    		}
    	} else if r.Method == http.MethodOptions {
    
    	} else {
    		// Respond with 418 I'm a teapot for non-GET requests
    		w.WriteHeader(http.StatusMethodNotAllowed)
    	}
    }
    
    // sortData sorts the machinedetails into the given interval, from "since" days ago to "start" days ago (since has to be greater than start)
    // The duration is at the END of the interval, i.e a given avg amp hour is valid the <interval> minutes before the timestamp
    func sortData(machines []other.MachineDetails, interval int64, since int64, start int64) ([]other.MachineDetails, error) {
    	var intervalMinutes = time.Minute * time.Duration(interval)
    	var intervalList []time.Time
    	startTime := time.Now().Add(-time.Hour * 24 * time.Duration(since)) // when to start the packets
    	endTime := time.Now().Add(-time.Hour * 24 * time.Duration(start))   // when to stop the packets
    	var currentIntervalTime = startTime
    	//create list of timestamps so each of the machines use the same intervals
    	for {
    		currentIntervalTime = currentIntervalTime.Add(intervalMinutes)
    		intervalList = append(intervalList, currentIntervalTime)
    
    		if currentIntervalTime.Add(intervalMinutes).After(endTime) {
    			break
    		}
    	}
    	//loop over each machine using a for loop
    	for k := range machines {
    		quickSort(machines[k].SensorDataList, 0, len(machines[k].SensorDataList)-1)
    
    		var packetList []other.SensorData     //new list of packets
    		var tempPacketList []other.SensorData //temporary list of packets, to make sure it is averaged correctly
    		var i = 0
    
    		for _, tInterval := range intervalList {
    
    			for i < len(machines[k].SensorDataList) {
    				//println(i)
    				if machines[k].SensorDataList[i].DateTime.Before(startTime) { //if the packet has not yet reached the start time, go to next packet
    					i++
    					continue
    				}
    				if machines[k].SensorDataList[i].DateTime.After(tInterval) { //if the packet has passed the current interval, go to next interval
    					break
    				}
    
    				//if nothing else, the packet is within the current interval
    				tempPacketList = append(tempPacketList, machines[k].SensorDataList[i])
    				i++
    			}
    
    			var acu float64 = 0
    			var avgCurr float64 = 0
    			var offMAX float64 = 0
    			var offMIN float64 = 0
    			var volt float64 = 0
    			var temp float64 = 0
    			if len(tempPacketList) > 0 {
    				//summarize the packet info, to create average below
    				for _, p := range tempPacketList {
    					acu = p.Acumulation
    					avgCurr += p.AVGCurrent
    					offMAX += p.OffsetMax
    					offMIN += p.OffsetMin
    					volt += p.Voltage
    					temp += p.Temperature
    				}
    				//create a packet that contains the averages of the previous packets data, even if it is zero (in the else clause), so every machine has equal len of data
    				newPacket := other.SensorData{
    					Acumulation: acu,
    					AVGCurrent:  avgCurr / float64(len(tempPacketList)),
    					OffsetMax:   offMAX / float64(len(tempPacketList)),
    					OffsetMin:   offMIN / float64(len(tempPacketList)),
    					Voltage:     volt / float64(len(tempPacketList)),
    					Temperature: temp / float64(len(tempPacketList)),
    					DateTime:    tInterval,
    				}
    				packetList = append(packetList, newPacket)
    			} else {
    				newPacket := other.SensorData{
    					Acumulation: acu,
    					AVGCurrent:  avgCurr,
    					OffsetMax:   offMAX,
    					OffsetMin:   offMIN,
    					Voltage:     volt,
    					Temperature: temp,
    					DateTime:    tInterval,
    				}
    				packetList = append(packetList, newPacket)
    			}
    			tempPacketList = nil
    		}
    		//set the sensorData list to the machine
    		machines[k].SensorDataList = packetList
    		//empty the list, so old data is not reused
    		packetList = nil
    	}
    	return machines, nil
    }
    
    // sorts the list of SensorData by time, in ascending order, index low being the earliest time, index high being the latest
    // implementation of quicksort
    
    func quickSort(arr []other.SensorData, low int, high int) {
    	if low < high {
    		pi := partion(arr, low, high)
    
    		// Recursively sort elements before partition and after partition
    		quickSort(arr, low, pi-1)
    		quickSort(arr, pi+1, high)
    	}
    }
    
    func partion(arr []other.SensorData, low int, high int) int {
    	pivot := arr[high]
    	i := low - 1
    
    	for j := low; j < high; j++ {
    		if arr[j].DateTime.Before(pivot.DateTime) {
    			i++
    
    			arr[i], arr[j] = arr[j], arr[i]
    		}
    	}
    
    	arr[i+1], arr[high] = arr[high], arr[i+1]
    	return i + 1
    }
    
    // function to transform machinedetails struct into data for frontend
    func setIntoDatasets(Machines []other.MachineDetails) other.OverviewData {
    	oData := other.OverviewData{}
    	doneOnce := false
    
    	for _, machine := range Machines {
    		dataset := createDataset(machine)
    		OMachine := other.OMachine{Name: machine.Machine.Name, EUI: machine.Machine.EUI, Dataset: dataset}
    		if !containsBuilding(machine.Building.Name, oData.Buildings) {
    			oData.Buildings = append(oData.Buildings, other.OBuilding{Name: machine.Building.Name})
    		}
    		for idx, building := range oData.Buildings {
    			if machine.Building.Name == building.Name {
    				if !containsDepartment(machine.Department.Name, building.Departments) {
    					if machine.Department.Name == "" && !containsDepartment("No department", building.Departments) {
    						oData.Buildings[idx].Departments = append(building.Departments, other.ODepartment{Name: "No department"})
    					} else {
    						oData.Buildings[idx].Departments = append(building.Departments, other.ODepartment{Name: machine.Department.Name})
    					}
    				}
    			}
    		}
    		for idx, building := range oData.Buildings {
    			for dIdx, department := range building.Departments {
    				if department.Name == machine.Department.Name || (department.Name == "No department" && machine.Department.Name == "") {
    					oData.Buildings[idx].Departments[dIdx].Machines = append(department.Machines, OMachine)
    					if !doneOnce {
    						var timeList []time.Time
    						for _, data := range machine.SensorDataList {
    							timeList = append(timeList, data.DateTime)
    						}
    						oData.Labels = timeList
    						doneOnce = true //only set the labels on the first machine
    					}
    				}
    			}
    		}
    		//loop over all the current machines associated process
    		for _, process := range machine.AssociatedProcesses {
    			found, idx := containsProcess(process.Name, oData.Processes)
    			if !found {
    				if process.Name != "" {
    					tempProcess := other.OProcess{Name: process.Name}
    					tempProcess.Machines = append(tempProcess.Machines, OMachine)
    					oData.Processes = append(oData.Processes, tempProcess)
    				}
    			} else {
    				oData.Processes[idx].Machines = append(oData.Processes[idx].Machines, OMachine)
    			}
    		}
    	}
    
    	for bIdx, building := range oData.Buildings {
    		buildingDataset := other.Dataset{Label: building.Name}
    		var allDepartments []other.Dataset
    
    		for dIdx, department := range building.Departments {
    			var allMachines []other.Dataset
    			for _, machine := range department.Machines {
    				allMachines = append(allMachines, machine.Dataset)
    			}
    			oData.Buildings[bIdx].Departments[dIdx].Dataset = avgDatasets(allMachines)
    			oData.Buildings[bIdx].Departments[dIdx].Dataset.Label = department.Name
    			allDepartments = append(allDepartments, oData.Buildings[bIdx].Departments[dIdx].Dataset)
    		}
    		buildingDataset = avgDatasets(allDepartments)
    		buildingDataset.Label = building.Name
    		oData.Buildings[bIdx].Dataset = buildingDataset
    	}
    
    	for pIdx, process := range oData.Processes {
    		processDataset := other.Dataset{Label: process.Name}
    		var allMachines []other.Dataset
    
    		for _, machine := range process.Machines {
    			allMachines = append(allMachines, machine.Dataset)
    		}
    		processDataset = avgDatasets(allMachines)
    		oData.Processes[pIdx].Dataset = processDataset
    	}
    
    	return oData
    }
    
    // turns a "MachineDetails" into a dataset
    func createDataset(details other.MachineDetails) other.Dataset {
    	dataset := other.Dataset{Label: details.Machine.Name}
    	for _, data := range details.SensorDataList {
    		dataset.Data = append(dataset.Data, data.AVGCurrent*float64(details.Machine.Voltage))
    	}
    	return dataset
    }
    
    // averages a list of datasets
    func avgDatasets(datasets []other.Dataset) other.Dataset {
    	retDataset := other.Dataset{}
    	total := 0
    	for _, dataset := range datasets {
    		for idx, data := range dataset.Data {
    			if len(retDataset.Data) < idx+1 {
    				retDataset.Data = append(retDataset.Data, data)
    			} else {
    				retDataset.Data[idx] += data
    			}
    		}
    		total++
    	}
    	for _, data := range retDataset.Data {
    		data = data / float64(total)
    	}
    	return retDataset
    }
    
    func containsBuilding(name string, buildings []other.OBuilding) bool {
    	for _, building := range buildings {
    		if building.Name == name {
    			return true
    		}
    	}
    	return false
    }
    
    func containsDepartment(name string, departments []other.ODepartment) bool {
    	for _, building := range departments {
    		if building.Name == name {
    			return true
    		}
    	}
    	return false
    }
    
    func containsProcess(name string, processes []other.OProcess) (bool, int) {
    	for idx, building := range processes {
    		if building.Name == name {
    			return true, idx
    		}
    	}
    	return false, -1
    }