diff --git a/pool/base.php b/pool/base.php
index 4a64a75e..5b24c4ab 100644
--- a/pool/base.php
+++ b/pool/base.php
@@ -63,8 +63,9 @@ function howlongago($sec)
return $des;
}
#
-function howmanyhrs($sec)
+function howmanyhrs($tot)
{
+ $sec = round($tot);
if ($sec < 60)
$des = $sec.'s';
else
@@ -89,6 +90,11 @@ function btcfmt($amt)
return number_format($amt, 8);
}
#
+function utcd($when)
+{
+ return gmdate('Y-m-d H:i:s+00', round($when));
+}
+#
global $sipre;
# max of uint64 is ~1.845x10^19, 'Z' is above that (10^21)
# max of uint256 is ~1.158x10^77, which is well above 'Y' (10^24)
diff --git a/pool/db.php b/pool/db.php
index cd9d347c..6c85e9cd 100644
--- a/pool/db.php
+++ b/pool/db.php
@@ -270,6 +270,18 @@ function getMPayouts($user)
return repDecode($rep);
}
#
+function getShifts($user)
+{
+ if ($user == false)
+ showIndex();
+ $flds = array('username' => $user);
+ $msg = msgEncode('shifts', 'shift', $flds, $user);
+ $rep = sendsockreply('getShifts', $msg);
+ if (!$rep)
+ dbdown();
+ return repDecode($rep);
+}
+#
function getBlocks($user)
{
if ($user == false)
diff --git a/pool/page_blocks.php b/pool/page_blocks.php
index 4040ed5b..7d8a35e8 100644
--- a/pool/page_blocks.php
+++ b/pool/page_blocks.php
@@ -184,7 +184,7 @@ function doblocks($data, $user)
if ($user !== null)
$pg .= "
".htmlspecialchars($ans['workername:'.$i]).' | ';
$pg .= "".btcfmt($ans['reward:'.$i]).' | ';
- $pg .= "".gmdate('Y-m-d H:i:s+00', $ans['firstcreatedate:'.$i]).' | ';
+ $pg .= "".utcd($ans['firstcreatedate:'.$i]).' | ';
$pg .= "".$stat.' | ';
$pg .= "$stara$acc | ";
$pg .= "$bpct | ";
diff --git a/pool/page_shifts.php b/pool/page_shifts.php
new file mode 100644
index 00000000..0704bf02
--- /dev/null
+++ b/pool/page_shifts.php
@@ -0,0 +1,62 @@
+\n";
+ $pg .= "";
+ $pg .= "Shift | ";
+ $pg .= "Start | ";
+ $pg .= "Elapsed | ";
+ $pg .= "Your Diff | ";
+ $pg .= "Avg Hs | ";
+ $pg .= "Shares | ";
+ $pg .= "Avg Share | ";
+ $pg .= "
\n";
+ if ($ans['STATUS'] != 'ok')
+ $pg = 'Shifts
'.$pg;
+ else
+ {
+ $count = $ans['rows'];
+ $pg = 'Last '.($count+1).' Shifts
'.$pg;
+ for ($i = 0; $i < $count; $i++)
+ {
+ if (($i % 2) == 0)
+ $row = 'even';
+ else
+ $row = 'odd';
+
+ $pg .= "";
+ $shif = preg_replace(array('/^.* to /','/^.*fin: /'), '', $ans['shift:'.$i]);
+ $pg .= "$shif | ";
+ $start = $ans['start:'.$i];
+ $pg .= ''.utcd($start).' | ';
+ $nd = $ans['end:'.$i];
+ $elapsed = $nd - $start;
+ $pg .= ''.howmanyhrs($elapsed).' | ';
+ $diffacc = $ans['diffacc:'.$i];
+ $pg .= ''.difffmt($diffacc).' | ';
+ $hr = $diffacc * pow(2,32) / $elapsed;
+ $pg .= ''.dsprate($hr).' | ';
+ $shareacc = $ans['shareacc:'.$i];
+ $pg .= ''.difffmt($shareacc).' | ';
+ if ($shareacc > 0)
+ $avgsh = $diffacc / $shareacc;
+ else
+ $avgsh = 0;
+ $pg .= ''.number_format($avgsh, 2).' | ';
+ $pg .= "
\n";
+ }
+ }
+ $pg .= "\n";
+
+ return $pg;
+}
+#
+function show_shifts($info, $page, $menu, $name, $user)
+{
+ gopage($info, NULL, 'doshifts', $page, $menu, $name, $user);
+}
+#
+?>
diff --git a/pool/prime.php b/pool/prime.php
index 56973cda..ced6f53d 100644
--- a/pool/prime.php
+++ b/pool/prime.php
@@ -73,6 +73,7 @@ function check()
'User Settings' => 'userset'
),
'Workers' => array(
+ 'Shifts ' => 'shifts',
'Workers ' => 'workers',
'Management' => 'workmgt',
),
diff --git a/src/ckdb.c b/src/ckdb.c
index 2c8d44c3..8ad9a05e 100644
--- a/src/ckdb.c
+++ b/src/ckdb.c
@@ -2665,6 +2665,7 @@ static void *socketer(__maybe_unused void *arg)
case CMD_PPLNS2:
case CMD_PAYOUTS:
case CMD_MPAYOUTS:
+ case CMD_SHIFTS:
case CMD_DSP:
case CMD_BLOCKSTATUS:
if (!startup_complete) {
@@ -2885,6 +2886,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
case CMD_PPLNS2:
case CMD_PAYOUTS:
case CMD_MPAYOUTS:
+ case CMD_SHIFTS:
case CMD_USERSTATUS:
case CMD_MARKS:
LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored",
diff --git a/src/ckdb.h b/src/ckdb.h
index 35de904e..a0646a2d 100644
--- a/src/ckdb.h
+++ b/src/ckdb.h
@@ -52,7 +52,7 @@
#define DB_VLOCK "1"
#define DB_VERSION "1.0.0"
-#define CKDB_VERSION DB_VERSION"-1.010"
+#define CKDB_VERSION DB_VERSION"-1.011"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@@ -343,6 +343,7 @@ enum cmd_values {
CMD_PPLNS2,
CMD_PAYOUTS,
CMD_MPAYOUTS,
+ CMD_SHIFTS,
CMD_USERSTATUS,
CMD_MARKS,
CMD_END
@@ -1719,6 +1720,7 @@ extern int32_t _coinbase1height(char *coinbase1, WHERE_FFL_ARGS);
extern cmp_t _cmp_height(char *coinbase1a, char *coinbase1b, WHERE_FFL_ARGS);
extern cmp_t cmp_workinfo_height(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_workinfo(int64_t workinfoid, K_TREE_CTX *ctx);
+extern K_ITEM *next_workinfo(int64_t workinfoid, K_TREE_CTX *ctx);
extern bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance,
char *by, char *code, char *inet, tv_t *cd,
tv_t *ss_first, tv_t *ss_last, int64_t *ss_count,
diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c
index 31659147..303be5cd 100644
--- a/src/ckdb_cmd.c
+++ b/src/ckdb_cmd.c
@@ -4372,7 +4372,7 @@ static char *cmd_mpayouts(__maybe_unused PGconn *conn, char *cmd, char *id,
po_item = prev_in_ktree(ctx);
DATA_PAYOUTS_NULL(payouts, po_item);
}
- K_RUNLOCK(miningpayouts_free);
+ K_RUNLOCK(payouts_free);
snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
rows, FLDSEP,
@@ -4387,6 +4387,176 @@ static char *cmd_mpayouts(__maybe_unused PGconn *conn, char *cmd, char *id,
return buf;
}
+static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
+ __maybe_unused tv_t *now, __maybe_unused char *by,
+ __maybe_unused char *code, __maybe_unused char *inet,
+ __maybe_unused tv_t *notcd,
+ __maybe_unused K_TREE *trf_root)
+{
+ K_ITEM *i_username, *u_item, ms_look, *wm_item, *ms_item, *wi_item;
+ K_TREE_CTX wm_ctx[1], ms_ctx[1];
+ WORKMARKERS *wm;
+ WORKINFO *wi;
+ MARKERSUMMARY markersummary, *ms, ms_add;
+ USERS *users;
+ char reply[1024] = "";
+ char tmp[1024];
+ size_t siz = sizeof(reply);
+ char *buf;
+ size_t len, off;
+ tv_t marker_end = { 0L, 0L };
+ int rows;
+
+ LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
+
+ i_username = require_name(trf_root, "username", 3, (char *)userpatt, reply, siz);
+ if (!i_username)
+ return strdup(reply);
+
+ K_RLOCK(users_free);
+ u_item = find_users(transfer_data(i_username));
+ K_RUNLOCK(users_free);
+ if (!u_item)
+ return strdup("bad");
+ DATA_USERS(users, u_item);
+
+ APPEND_REALLOC_INIT(buf, off, len);
+ APPEND_REALLOC(buf, off, len, "ok.");
+ INIT_MARKERSUMMARY(&ms_look);
+ ms_look.data = (void *)(&markersummary);
+ rows = 0;
+ K_RLOCK(workmarkers_free);
+ wm_item = last_in_ktree(workmarkers_workinfoid_root, wm_ctx);
+ DATA_WORKMARKERS_NULL(wm, wm_item);
+ /* TODO: allow to see details of a single payoutid
+ * if it has multiple items (percent payout user) */
+ while (rows < 98 && wm_item) {
+ if (CURRENT(&(wm->expirydate)) && WMPROCESSED(wm->status)) {
+ K_RUNLOCK(workmarkers_free);
+
+ bzero(&ms_add, sizeof(ms_add));
+
+ markersummary.markerid = wm->markerid;
+ markersummary.userid = users->userid;
+ markersummary.workername = EMPTY;
+ K_RLOCK(markersummary_free);
+ ms_item = find_after_in_ktree(markersummary_root, &ms_look,
+ cmp_markersummary, ms_ctx);
+ DATA_MARKERSUMMARY_NULL(ms, ms_item);
+ while (ms_item && ms->markerid == wm->markerid &&
+ ms->userid == users->userid) {
+ ms_add.diffacc += ms->diffacc;
+ ms_add.diffrej += ms->diffrej;
+ ms_add.shareacc += ms->shareacc;
+ ms_add.sharerej += ms->sharerej;
+
+ ms_item = next_in_ktree(ms_ctx);
+ DATA_MARKERSUMMARY_NULL(ms, ms_item);
+ }
+ K_RUNLOCK(markersummary_free);
+
+ if (marker_end.tv_sec == 0L) {
+ wi_item = next_workinfo(wm->workinfoidend, NULL);
+ if (!wi_item) {
+ /* There's no workinfo after this shift
+ * Unexpected ... estimate last wid+30s */
+ wi_item = find_workinfo(wm->workinfoidend, NULL);
+ if (!wi_item) {
+ // Nothing is currently locked
+ LOGERR("%s() workmarker %"PRId64"/%s."
+ " missing widend %"PRId64,
+ __func__, wm->markerid,
+ wm->description,
+ wm->workinfoidend);
+ snprintf(reply, siz, "data error 1");
+ return strdup(reply);
+ }
+ DATA_WORKINFO(wi, wi_item);
+ copy_tv(&marker_end, &(wi->createdate));
+ marker_end.tv_sec += 30;
+ } else {
+ DATA_WORKINFO(wi, wi_item);
+ copy_tv(&marker_end, &(wi->createdate));
+ }
+ }
+
+ wi_item = find_workinfo(wm->workinfoidstart, NULL);
+ if (!wi_item) {
+ // Nothing is currently locked
+ LOGERR("%s() workmarker %"PRId64"/%s. missing "
+ "widstart %"PRId64,
+ __func__, wm->markerid, wm->description,
+ wm->workinfoidstart);
+ snprintf(reply, siz, "data error 2");
+ return strdup(reply);
+ }
+ DATA_WORKINFO(wi, wi_item);
+
+ bigint_to_buf(wm->markerid, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "markerid:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ str_to_buf(wm->description, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "shift:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ ftv_to_buf(&(wi->createdate), reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "start:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ ftv_to_buf(&marker_end, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "end:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ double_to_buf(ms_add.diffacc, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "diffacc:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ double_to_buf(ms_add.diffrej, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "diffrej:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ double_to_buf(ms_add.shareacc, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "shareacc:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ double_to_buf(ms_add.sharerej, reply, sizeof(reply));
+ snprintf(tmp, sizeof(tmp), "sharerej:%d=%s%c",
+ rows, reply, FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ rows++;
+
+ // Setup for next shift
+ copy_tv(&marker_end, &(wi->createdate));
+
+ K_RLOCK(workmarkers_free);
+ }
+ wm_item = prev_in_ktree(wm_ctx);
+ DATA_WORKMARKERS_NULL(wm, wm_item);
+ }
+ K_RUNLOCK(workmarkers_free);
+
+ snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
+ rows, FLDSEP,
+ "markerid,shift,start,end,diffacc,diffrej,shareacc,sharerej",
+ FLDSEP);
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Shifts", FLDSEP, "");
+ APPEND_REALLOC(buf, off, len, tmp);
+
+ LOGDEBUG("%s.ok.%s", id, transfer_data(i_username));
+ return buf;
+}
+
static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd,
char *id, __maybe_unused tv_t *now,
__maybe_unused char *by, __maybe_unused char *code,
@@ -5049,6 +5219,7 @@ struct CMDS ckdb_cmds[] = {
{ CMD_PPLNS2, "pplns2", false, false, cmd_pplns2, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_PAYOUTS, "payouts", false, false, cmd_payouts, ACCESS_SYSTEM },
{ CMD_MPAYOUTS, "mpayouts", false, false, cmd_mpayouts, ACCESS_SYSTEM ACCESS_WEB },
+ { CMD_SHIFTS, "shifts", false, false, cmd_shifts, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_MARKS, "marks", false, false, cmd_marks, ACCESS_SYSTEM },
{ CMD_END, NULL, false, false, NULL, NULL }
diff --git a/src/ckdb_data.c b/src/ckdb_data.c
index 96649aa8..1cf72429 100644
--- a/src/ckdb_data.c
+++ b/src/ckdb_data.c
@@ -1569,6 +1569,34 @@ K_ITEM *find_workinfo(int64_t workinfoid, K_TREE_CTX *ctx)
return item;
}
+K_ITEM *next_workinfo(int64_t workinfoid, K_TREE_CTX *ctx)
+{
+ WORKINFO workinfo, *wi;
+ K_TREE_CTX ctx0[1];
+ K_ITEM look, *item;
+
+ if (ctx == NULL)
+ ctx = ctx0;
+
+ workinfo.workinfoid = workinfoid;
+ workinfo.expirydate.tv_sec = default_expiry.tv_sec;
+ workinfo.expirydate.tv_usec = default_expiry.tv_usec;
+
+ INIT_WORKINFO(&look);
+ look.data = (void *)(&workinfo);
+ K_RLOCK(workinfo_free);
+ item = find_after_in_ktree(workinfo_root, &look, cmp_workinfo, ctx);
+ if (item) {
+ DATA_WORKINFO(wi, item);
+ while (item && !CURRENT(&(wi->expirydate))) {
+ item = next_in_ktree(ctx);
+ DATA_WORKINFO_NULL(wi, item);
+ }
+ }
+ K_RUNLOCK(workinfo_free);
+ return item;
+}
+
bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance,
char *by, char *code, char *inet, tv_t *cd,
tv_t *ss_first, tv_t *ss_last, int64_t *ss_count,