Improve speed calculation, clean some things up

This commit is contained in:
Ken-Håvard Lieng 2020-05-18 03:21:34 +02:00
parent b92f5cfb43
commit a90e8d4b2f
2 changed files with 81 additions and 75 deletions

View File

@ -18,7 +18,7 @@ import (
type DCCSend struct { type DCCSend struct {
File string `json:"file"` File string `json:"file"`
IP string `json:"ip"` IP string `json:"ip"`
Port uint16 `json:"port"` Port string `json:"port"`
Length uint64 `json:"length"` Length uint64 `json:"length"`
} }
@ -27,14 +27,14 @@ func ParseDCCSend(ctcp *CTCP) *DCCSend {
if len(params) > 4 { if len(params) > 4 {
ip, err := strconv.Atoi(params[2]) ip, err := strconv.Atoi(params[2])
port, err := strconv.Atoi(params[3])
length, err := strconv.Atoi(params[4])
if err != nil { if err != nil {
return nil return nil
} }
ip3 := uint32ToIP(ip) length, err := strconv.ParseUint(params[4], 10, 64)
if err != nil {
return nil
}
filename := path.Base(params[1]) filename := path.Base(params[1])
if filename == "/" || filename == "." { if filename == "/" || filename == "." {
@ -43,9 +43,9 @@ func ParseDCCSend(ctcp *CTCP) *DCCSend {
return &DCCSend{ return &DCCSend{
File: filename, File: filename,
IP: ip3, IP: intToIP(ip),
Port: uint16(port), Port: params[3],
Length: uint64(length), Length: length,
} }
} }
@ -57,97 +57,99 @@ func (c *Client) Download(pack *DCCSend) {
// TODO: ask user if he/she wants to download the file // TODO: ask user if he/she wants to download the file
return return
} }
c.Progress <- DownloadProgress{ c.Progress <- DownloadProgress{
PercCompletion: 0, File: pack.File,
File: pack.File,
} }
file, err := os.OpenFile(filepath.Join(c.DownloadFolder, pack.File), os.O_CREATE|os.O_WRONLY, 0644) file, err := os.OpenFile(filepath.Join(c.DownloadFolder, pack.File), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
c.Progress <- DownloadProgress{ c.downloadFailed(pack, err)
PercCompletion: -1,
File: pack.File,
Error: err,
}
return return
} }
defer file.Close() defer file.Close()
con, err := net.Dial("tcp", fmt.Sprintf("%s:%d", pack.IP, pack.Port)) conn, err := net.Dial("tcp", net.JoinHostPort(pack.IP, pack.Port))
if err != nil { if err != nil {
c.Progress <- DownloadProgress{ c.downloadFailed(pack, err)
PercCompletion: -1,
File: pack.File,
Error: err,
}
return return
} }
defer conn.Close()
defer con.Close()
var speed float64
var prevUpdate time.Time
secondsElapsed := int64(0)
totalBytes := uint64(0) totalBytes := uint64(0)
buf := make([]byte, 0, 4*1024) accBytes := uint64(0)
start := time.Now().UnixNano() averageSpeed := float64(0)
buf := make([]byte, 4*1024)
start := time.Now()
prevUpdate := start
for { for {
n, err := con.Read(buf[:cap(buf)]) n, err := conn.Read(buf)
buf = buf[:n] if err != nil {
if n == 0 { if err != io.EOF {
if err == nil { c.downloadFailed(pack, err)
continue return
} }
if err == io.EOF { if n == 0 {
break break
} }
} }
if _, err := file.Write(buf); err != nil { if _, err := file.Write(buf[:n]); err != nil {
c.Progress <- DownloadProgress{ c.downloadFailed(pack, err)
PercCompletion: -1,
File: pack.File,
Error: err,
}
return return
} }
cycleBytes := uint64(len(buf)) accBytes += uint64(n)
totalBytes += cycleBytes totalBytes += uint64(n)
percentage := round2(100 * float64(totalBytes) / float64(pack.Length))
now := time.Now().UnixNano() conn.Write(uint64Bytes(totalBytes))
secondsElapsed = (now - start) / 1e9
speed = round2(float64(totalBytes) / (float64(secondsElapsed)))
secondsToGo := round2((float64(pack.Length) - float64(totalBytes)) / speed)
con.Write(byteRead(totalBytes)) if dt := time.Since(prevUpdate); dt >= time.Second {
if time.Since(prevUpdate) >= time.Second {
prevUpdate = time.Now() prevUpdate = time.Now()
speed := float64(accBytes) / dt.Seconds()
if averageSpeed == 0 {
averageSpeed = speed
} else {
averageSpeed = 0.2*speed + 0.8*averageSpeed
}
accBytes = 0
bytesRemaining := float64(pack.Length - totalBytes)
percentage := 100 * (float64(totalBytes) / float64(pack.Length))
c.Progress <- DownloadProgress{ c.Progress <- DownloadProgress{
Speed: humanReadableByteCount(speed, true), Speed: humanReadableByteCount(averageSpeed, true),
PercCompletion: percentage, PercCompletion: percentage,
BytesRemaining: humanReadableByteCount(float64(pack.Length-totalBytes), false), BytesRemaining: humanReadableByteCount(bytesRemaining, false),
BytesCompleted: humanReadableByteCount(float64(totalBytes), false), BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsElapsed, SecondsElapsed: secondsSince(start),
SecondsToGo: secondsToGo, SecondsToGo: bytesRemaining / averageSpeed,
File: pack.File, File: pack.File,
} }
} }
} }
con.Write(byteRead(totalBytes)) // TODO: is this needed?
conn.Write(uint64Bytes(totalBytes))
c.Progress <- DownloadProgress{ c.Progress <- DownloadProgress{
Speed: humanReadableByteCount(speed, true),
PercCompletion: 100, PercCompletion: 100,
BytesCompleted: humanReadableByteCount(float64(totalBytes), false), BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsElapsed, SecondsElapsed: secondsSince(start),
File: pack.File, File: pack.File,
} }
} }
func (c *Client) downloadFailed(pack *DCCSend, err error) {
c.Progress <- DownloadProgress{
PercCompletion: -1,
File: pack.File,
Error: err,
}
}
type DownloadProgress struct { type DownloadProgress struct {
File string `json:"file"` File string `json:"file"`
Error error `json:"error"` Error error `json:"error"`
@ -167,7 +169,7 @@ func (p DownloadProgress) ToJSON() string {
return string(progress) return string(progress)
} }
func uint32ToIP(n int) string { func intToIP(n int) string {
var byte1 = n & 255 var byte1 = n & 255
var byte2 = ((n >> 8) & 255) var byte2 = ((n >> 8) & 255)
var byte3 = ((n >> 16) & 255) var byte3 = ((n >> 16) & 255)
@ -175,14 +177,14 @@ func uint32ToIP(n int) string {
return fmt.Sprintf("%d.%d.%d.%d", byte4, byte3, byte2, byte1) return fmt.Sprintf("%d.%d.%d.%d", byte4, byte3, byte2, byte1)
} }
func byteRead(totalBytes uint64) []byte { func uint64Bytes(i uint64) []byte {
b := make([]byte, 8) b := make([]byte, 8)
binary.BigEndian.PutUint64(b, totalBytes) binary.BigEndian.PutUint64(b, i)
return b return b
} }
func round2(source float64) float64 { func secondsSince(t time.Time) int64 {
return math.Round(100*source) / 100 return int64(math.Round(time.Since(t).Seconds()))
} }
const ( const (

View File

@ -65,20 +65,16 @@ func (i *ircHandler) run() {
} }
case progress := <-i.client.Progress: case progress := <-i.client.Progress:
if progress.PercCompletion == 100 { if progress.Error != nil {
i.state.sendJSON("pm", Message{ i.sendDCCInfo("%s: Download failed (%s)", progress.File, progress.Error)
Server: i.client.Host, } else if progress.PercCompletion == 100 {
From: "@dcc", i.sendDCCInfo("Download finished, get it here: %s://%s/downloads/%s/%s",
Content: fmt.Sprintf("%s://%s/downloads/%s/%s", i.state.String("scheme"), i.state.String("scheme"), i.state.String("host"), i.state.user.Username, progress.File)
i.state.String("host"), i.state.user.Username, progress.File), } else if progress.PercCompletion == 0 {
}) i.sendDCCInfo("%s: Starting download", progress.File)
} else { } else {
i.state.sendJSON("pm", Message{ i.sendDCCInfo("%s: %.1f%%, %s, %s remaining, %.1fs left", progress.File,
Server: i.client.Host, progress.PercCompletion, progress.Speed, progress.BytesRemaining, progress.SecondsToGo)
From: "@dcc",
Content: fmt.Sprintf("%s: %.2f%% %s remaining, %.2fs left", progress.File,
progress.PercCompletion, progress.BytesRemaining, progress.SecondsToGo),
})
} }
} }
} }
@ -435,6 +431,14 @@ func (i *ircHandler) log(v ...interface{}) {
log.Println("[IRC]", i.state.user.ID, i.client.Host, s[:len(s)-1]) log.Println("[IRC]", i.state.user.ID, i.client.Host, s[:len(s)-1])
} }
func (i *ircHandler) sendDCCInfo(message string, a ...interface{}) {
i.state.sendJSON("pm", Message{
Server: i.client.Host,
From: "@dcc",
Content: fmt.Sprintf(message, a...),
})
}
func parseMode(mode string) *Mode { func parseMode(mode string) *Mode {
m := Mode{} m := Mode{}
add := false add := false