Browse Source

block charts

master
yuriy0803 5 years ago
parent
commit
9229fac02a
  1. 2
      api/server.go
  2. 48
      storage/redis.go
  3. 177
      www/app/controllers/blocks.js
  4. 1
      www/app/templates/blocks.hbs

2
api/server.go

@ -204,6 +204,7 @@ func (s *ApiServer) collectStats() {
}
if len(s.config.LuckWindow) > 0 {
stats["luck"], err = s.backend.CollectLuckStats(s.config.LuckWindow)
stats["luckCharts"], err = s.backend.CollectLuckCharts(s.config.LuckWindow[0])
if err != nil {
log.Printf("Failed to fetch luck stats from backend: %v", err)
return
@ -301,6 +302,7 @@ func (s *ApiServer) BlocksIndex(w http.ResponseWriter, r *http.Request) {
reply["candidates"] = stats["candidates"]
reply["candidatesTotal"] = stats["candidatesTotal"]
reply["luck"] = stats["luck"]
reply["luckCharts"] = stats["luckCharts"]
}
err := json.NewEncoder(w).Encode(reply)

48
storage/redis.go

@ -4,6 +4,7 @@ import (
"fmt"
"math"
"math/big"
"sort"
"strconv"
"strings"
"time"
@ -46,6 +47,15 @@ type PaymentCharts struct {
Amount int64 `json:"amount"`
}
type LuckCharts struct {
Timestamp int64 `json:"x"`
Height int64 `json:"height"`
Difficulty int64 `json:"difficulty"`
Shares int64 `json:"shares"`
SharesDiff float64 `json:"sharesDiff"`
Reward string `json:"reward"`
}
type SumRewardData struct {
Interval int64 `json:"inverval"`
Reward int64 `json:"reward"`
@ -1102,6 +1112,44 @@ func (r *RedisClient) CollectLuckStats(windows []int) (map[string]interface{}, e
return stats, nil
}
func (r *RedisClient) CollectLuckCharts(max int) (stats []*LuckCharts, err error) {
var result []*LuckCharts
tx := r.client.Multi()
defer tx.Close()
cmds, err := tx.Exec(func() error {
tx.ZRevRangeWithScores(r.formatKey("blocks", "matured"), 0, int64(max-1))
return nil
})
if err != nil {
return result, err
}
blocks := convertBlockResults(cmds[0].(*redis.ZSliceCmd))
for i, block := range blocks {
if i > (max - 1) {
break
}
lc := LuckCharts{}
var sharesDiff = float64(block.TotalShares) / float64(block.Difficulty)
lc.Timestamp = block.Timestamp
lc.Height = block.RoundHeight
lc.Difficulty = block.Difficulty
lc.Shares = block.TotalShares
lc.SharesDiff = sharesDiff
lc.Reward = block.RewardString
result = append(result, &lc)
}
sort.Sort(TimestampSorter(result))
return result, nil
}
type TimestampSorter []*LuckCharts
func (a TimestampSorter) Len() int { return len(a) }
func (a TimestampSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a TimestampSorter) Less(i, j int) bool { return a[i].Timestamp < a[j].Timestamp }
func convertCandidateResults(raw *redis.ZSliceCmd) []*BlockData {
var result []*BlockData
for _, v := range raw.Val() {

177
www/app/controllers/blocks.js

@ -0,0 +1,177 @@
import Ember from 'ember';
export default Ember.Controller.extend({
applicationController: Ember.inject.controller('application'),
config: Ember.computed.reads('applicationController.config'),
settings: Ember.computed.reads('applicationController.model.settings'),
BlockUnlockDepth: Ember.computed('settings', {
get() {
var depth = this.get('settings.BlockUnlockDepth');
if (depth) {
return depth;
}
return this.get('config').BlockUnlockDepth;
}
}),
chartOptions: Ember.computed("model.luckCharts", {
get() {
var e = this,
t = e.getWithDefault("model.luckCharts"),
a = {
colors: ['#f45b5b', '#8085e9', '#8d4654', '#7798BF', '#aaeeee',
'#ff0066', '#eeaaee', '#55BF3B', '#DF5353', '#7798BF', '#aaeeee'],
chart: {
backgroundColor: "rgba(255, 255, 255, 0.1)",
marginRight: 10,
height: 200,
events: {
load: function() {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime(),
y = e.getWithDefault("model.luckCharts.difficulty");
series.addPoint([x, y], true, true);
}, 1090000000);
}
}
},
title: {
text: ""
},
xAxis: {
labels: {
style: {
color: '#6e6e70'
}
},
ordinal: false,
type: "datetime",
dateTimeLabelFormats: {
millisecond: "%H:%M:%S",
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%e. %b",
week: "%e. %b",
month: "%b '%y",
year: "%Y"
}
},
yAxis: {
labels: {
style: {
color: '#6e6e70'
}
},
title: {
text: "shares and difficulty",
style: {
color: 'black',
fontSize: '16px',
fontWeight: 'bold'
}
},
softMax: 100,
},
plotLines: [{
value: 0,
width: 1,
color: "#808080"
}],
plotOptions: {
series: {
shadow: true
},
candlestick: {
lineColor: '#404048'
},
map: {
shadow: false
}
},
legend: {
enabled: true
},
tooltip: {
formatter: function() {
var ss = this.y > 1000000000000 ? "<b>" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH</b>" : this.y > 1000000000 ? "<b>" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH</b>" : this.y > 1000000 ? "<b>" + (this.y / 1000000).toFixed(2) + "&nbsp;MH</b>" : this.y > 1000 ? "<b>" + (this.y / 1000).toFixed(2) + "&nbsp;KH</b>" : "<b>" + this.y.toFixed(2) + "&nbsp;H</b>";
return ss + "<br/><b>Number:&nbsp;" + this.point.h + "</b><br/><b>" + this.point.d + "</b><br/><b>Reward:&nbsp;" + (this.point.w/1000000000000000000).toFixed(8) + e.get('config.Unit') + " </b><br/><b>Variance:&nbsp;" + (this.point.s*100).toFixed(2)+ "%</b>";
},
useHTML: true
},
exporting: {
enabled: false
},
series: [{
step: 'center',
color: "#E99002",
name: "difficulty",
data: function() {
var e, a = [];
if (null != t) {
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].difficulty;
a.push({
x: r,
d: l,
h: t[e].height,
w: t[e].reward,
s: t[e].sharesDiff,
y: n
});
}
} else {
a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
}()
}, {
step: 'center',
name: "shares",
data: function() {
var e, a = [];
if (null != t) {
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].shares;
a.push({
x: r,
d: l,
h: t[e].height,
w: t[e].reward,
s: t[e].sharesDiff,
y: n
});
}
} else {
a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
}()
}]
};
return a;
}
})
});

1
www/app/templates/blocks.hbs

@ -9,6 +9,7 @@
</div>
<div class="container">
{{high-charts mode=stockChart chartOptions=chartOptions content=chartData}}
{{#if model.luck}}
{{partial "luck"}}
{{/if}}

Loading…
Cancel
Save