~ehmry/tonaltime

3ded2ade54ce9d4532755033ece161e043097a16 — Emery Hemingway 6 months ago 12a21f7 20240408
Syndicate API update
5 files changed, 264 insertions(+), 44 deletions(-)

M Tupfile
M Tuprules.tup
M lock.json
M src/tonaltime.nim
M tonaltime.nimble
M Tupfile => Tupfile +2 -1
@@ 1,2 1,3 @@
include_rules
: lock.json |> !nim_cfg |> | ./<lock>
: |> !nim_lk |> {lockfile}
: {lockfile} |> !nim_cfg |> | ./<lock>

M Tuprules.tup => Tuprules.tup +2 -1
@@ 1,3 1,4 @@
include ../syndicate-nim/depends.tup
NIM = $(DIRENV) $(NIM)
NIM_FLAGS += --path:$(TUP_CWD)/../syndicate-nim/src
NIM_GROUPS += $(TUP_CWD)/<lock>
\ No newline at end of file
NIM_GROUPS += $(TUP_CWD)/<lock>

M lock.json => lock.json +186 -1
@@ 1,1 1,186 @@
{"depends":[{"method":"fetchzip","packages":["hashlib"],"path":"/nix/store/v03nzlpdgbfxd2zhcnkfbkq01d5kqxcl-source","rev":"84e0247555e4488594975900401baaf5bbbfb531","sha256":"1pfczsv8kl36qpv543f93d2y2vgz2acckssfap7l51s2x62m6qwx","srcDir":"","url":"https://github.com/khchen/hashlib/archive/84e0247555e4488594975900401baaf5bbbfb531.tar.gz"},{"method":"fetchzip","packages":["nimcrypto"],"path":"/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source","ref":"traditional-api","rev":"602c5d20c69c76137201b5d41f788f72afb95aa8","sha256":"1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65","srcDir":"","url":"https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"},{"method":"fetchzip","packages":["npeg"],"path":"/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source","ref":"1.2.1","rev":"26d62fdc40feb84c6533956dc11d5ee9ea9b6c09","sha256":"0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh","srcDir":"src","url":"https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz"},{"method":"fetchzip","packages":["preserves"],"path":"/nix/store/nrcpzf9hx70kry3gwhrdzcs3qicjncjh-source","ref":"20231021","rev":"edece399be70818208bf2263c30cb2bcf435bbff","sha256":"0xmw35wmw3a4lja9q4qvlvpxv3xk0hnkjg4fwfw6f3inh6zfiqki","srcDir":"src","url":"https://git.syndicate-lang.org/ehmry/preserves-nim/archive/edece399be70818208bf2263c30cb2bcf435bbff.tar.gz"},{"method":"fetchzip","packages":["syndicate"],"path":"/nix/store/1y3nnpp2mhxqmdb3xh4c4k5k5l9hhqk3-source","ref":"20231019","rev":"57b99b20e7db1b97b1cb9c6df574bd13983c26fc","sha256":"1kgb3a78igs37xkmv8cbaxa17qdjf2h43vdmpda517c9086ggsn5","srcDir":"src","url":"https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/57b99b20e7db1b97b1cb9c6df574bd13983c26fc.tar.gz"}]}
{
  "depends": [
    {
      "method": "fetchzip",
      "packages": [
        "bigints"
      ],
      "path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
      "rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
      "sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
      "srcDir": "src",
      "url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "cps"
      ],
      "path": "/nix/store/8gbhwni0akqskdb3qhn5nfgv6gkdz0vz-source",
      "rev": "c90530ac57f98a842b7be969115c6ef08bdcc564",
      "sha256": "0h8ghs2fqg68j3jdcg7grnxssmllmgg99kym2w0a3vlwca1zvr62",
      "srcDir": "",
      "url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "getdns"
      ],
      "path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
      "rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
      "sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
      "srcDir": "src",
      "url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "getdns"
      ],
      "path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
      "rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
      "sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
      "srcDir": "src",
      "url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "hashlib"
      ],
      "path": "/nix/store/fav82xdbicvlk34nmcbl89zx99lr3mbs-source",
      "rev": "f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac",
      "sha256": "1sx6j952lj98629qfgr7ds5aipyw9d6lldcnnqs205wpj4pkcjb3",
      "srcDir": "",
      "url": "https://github.com/ehmry/hashlib/archive/f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "nimcrypto"
      ],
      "path": "/nix/store/fkrcpp8lzj2yi21na79xm63xk0ggnqsp-source",
      "rev": "f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5",
      "sha256": "1h3dzdbc9kacwpi10mj73yjglvn7kbizj1x8qc9099ax091cj5xn",
      "srcDir": "",
      "url": "https://github.com/cheatfate/nimcrypto/archive/f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "npeg"
      ],
      "path": "/nix/store/xpn694ibgipj8xak3j4bky6b3k0vp7hh-source",
      "rev": "ec0cc6e64ea4c62d2aa382b176a4838474238f8d",
      "sha256": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg",
      "srcDir": "src",
      "url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "preserves"
      ],
      "path": "/nix/store/2hy124xgabz134dxj3wji7mp47fdwy3w-source",
      "rev": "9ae435a83c6d5028405538af5d24a023af625b6e",
      "sha256": "1k7ywcp1a53x2fpc6wc2b0qzb264dkifash0s1wcp66rw3lx15k2",
      "srcDir": "src",
      "url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/9ae435a83c6d5028405538af5d24a023af625b6e.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "stew"
      ],
      "path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
      "rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
      "sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
      "srcDir": "",
      "url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "syndicate"
      ],
      "path": "/nix/store/kl628g7vg2ww8wilf8h2ag7qqnvvwdzb-source",
      "rev": "c2e1e2e0fa403529750196ce3ccb5a99a4d6c006",
      "sha256": "1r8ab79pgrrnzmp49h8rp50c9x8zd0p7bsvzxaphc221nvyfx09j",
      "srcDir": "src",
      "url": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/c2e1e2e0fa403529750196ce3ccb5a99a4d6c006.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "sys"
      ],
      "path": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source",
      "rev": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
      "sha256": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q",
      "srcDir": "src",
      "url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "sys"
      ],
      "path": "/nix/store/vf9ls2wip6d8xhsi3rjh0dqsqg597i6b-source",
      "rev": "c117ee60542f084525f254e6ade590675a6a2ed6",
      "sha256": "12qzx2lnh84xqfgypy0pka8nflq0y8n1izfwx8mb4zya5nzawmyf",
      "srcDir": "src",
      "url": "https://github.com/alaviss/nim-sys/archive/c117ee60542f084525f254e6ade590675a6a2ed6.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "taps"
      ],
      "path": "/nix/store/6y14ia52kr7jyaa0izx37mlablmq9s65-source",
      "rev": "8c8572cd971d1283e6621006b310993c632da247",
      "sha256": "1dp166bv9x773jmfqppg5i3v3rilgff013vb11yzwcid9l7s3iy8",
      "srcDir": "src",
      "url": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz"
    },
    {
      "date": "2024-04-02T15:38:57+01:00",
      "deepClone": false,
      "fetchLFS": false,
      "fetchSubmodules": true,
      "hash": "sha256-iZb9aAgYr4FGkqfIg49QWiCqeizIi047kFhugHiP8o0=",
      "leaveDotGit": false,
      "method": "git",
      "packages": [
        "solo5_dispatcher"
      ],
      "path": "/nix/store/sf5dgj2ljvahcm6my7d61ibda51vnrii-solo5_dispatcher",
      "rev": "a7a894a96a2221284012800e6fd32923d83d20bd",
      "sha256": "13gjixw80vjqj0xlx2y85ixal82sa27q7j57j9383bqq11lgv5l9",
      "srcDir": "pkg",
      "url": "https://git.sr.ht/~ehmry/solo5_dispatcher"
    },
    {
      "method": "fetchzip",
      "packages": [
        "cps"
      ],
      "path": "/nix/store/phdf6siqbhj7vx4qq507lzla81si60iz-source",
      "rev": "58772ff9ddb38a4b2ec52da142d8532ba2fe7039",
      "sha256": "1lph7v27nqwgm3a0ssi8q348gjrkjwgqc50agw38j7xif6wj80cw",
      "srcDir": "",
      "url": "https://github.com/ehmry/cps/archive/58772ff9ddb38a4b2ec52da142d8532ba2fe7039.tar.gz"
    },
    {
      "method": "fetchzip",
      "packages": [
        "stew"
      ],
      "path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
      "rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
      "sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
      "srcDir": "",
      "url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
    }
  ]
}

M src/tonaltime.nim => src/tonaltime.nim +72 -34
@@ 1,12 1,41 @@
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense

import std/[asyncdispatch, times]
import std/[oserrors, posix, times]
from std/strutils import toHex
import preserves, syndicate
from syndicate/relays import connectStdio
import pkg/sys/[handles, ioqueue]
import preserves, syndicate, syndicate/relays
import ./private/solartimes

type Time = posix.Time

{.pragma: timerfd, importc, header: "<sys/timerfd.h>".}

proc timerfd_create(clock_id: ClockId, flags: cint): cint {.timerfd, used.}
proc timerfd_settime(ufd: cint, flags: cint,
                      utmr: var Itimerspec, otmr: var Itimerspec): cint {.timerfd, used.}
proc timerfd_gettime(ufd: cint, curr: var Itimerspec): cint {.timerfd, used.}

var
  TFD_NONBLOCK {.timerfd.}: cint
  TFD_CLOEXEC {.timerfd.}: cint
  TFD_TIMER_ABSTIME {.timerfd.}: cint

proc newHexSecTimer(): cint =
  var its, old: Itimerspec
  result = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK or TFD_CLOEXEC)
  if result < 0:
    raiseOSError(osLastError(), "failed to acquire timer descriptor")
    # TODO: Align `its.it_value` to the next hexsec.
  if clock_gettime(CLOCK_REALTIME, its.it_value) < 0:
    raiseOSError(osLastError(), "clock_gettime")
  inc(its.it_value.tv_sec)
  # inc(its.it_value.tv_nsec, 1_000)
  its.it_interval.tv_sec = Time 1
  its.it_interval.tv_nsec = ((24 * 60 * 60 * 1_000_000_000) div 0x1_00_00) mod 1_000_000_000
  if timerfd_settime(result, TFD_TIMER_ABSTIME, its, old) < 0:
    raiseOSError(osLastError(), "failed to set timeout")

const milisecPerHexsec = (24 * 60 * 60 * 1_000) div 0x1_00_00

type


@@ 14,7 43,6 @@ type
    dataspace: Cap
    lat: float
    lon: float
    interval: float

  TonalTime {.preservesRecord: "tonaltime".} = object
    num: int64


@@ 28,33 56,43 @@ type
  Midnight  {.preservesRecord: "midnight".} = object
    s: string

runActor("tonaltime") do (root: Cap; turn: var Turn):
  connectStdio(turn, root)
  during(turn, root, ?Args) do (ds: Cap, interval: float, lat: float, lon: float):
  Clock = ref object
    facet: Facet
    ds: Cap
    midnight: DateTime
    timeHandle: syndicate.Handle

proc update(clock: Clock) =
  run(clock.facet) do (turn: var Turn):
    let
      horizon = horizonTimes(lon, lat, now())
      dayDur = horizon.sundown - horizon.sunrise
      noon = horizon.sunrise + (dayDur div 2)
      midnight = noon - initDuration(hours = 12)

    discard publish(turn, ds, Sunrise(s: $horizon.sunrise))
    discard publish(turn, ds, Sunset(s: $horizon.sundown))
    discard publish(turn, ds, Noon(s: $noon))
    discard publish(turn, ds, Midnight(s: $midnight))

    var timeHandle: Handle

    let facet = turn.facet
    proc publishTime {.gcsafe.} =
      facet.run do(turn: var Turn):
        let
          dt = now()
          milisecs = inMilliseconds (dt - midnight)
          hexsecs = milisecs div milisecPerHexsec
          hexStr = toHex(hexsecs shr 12, 1) & "_" & toHex(hexsecs shr 4, 2) & "_" & toHex(hexsecs, 1)
        replace(turn, ds, timeHandle, TonalTime(num: hexsecs, hex: hexStr))

        let timeout = int(milisecPerHexsec * interval)
        addTimer(timeout, oneshot = true) do (fd: AsyncFD) -> bool:
          publishTime()
    publishTime()
      dt = now()
      milisecs = inMilliseconds (dt - clock.midnight)
      hexsecs = (milisecs div milisecPerHexsec) and 0xFF_FF
      hexStr = toHex(hexsecs shr 12, 1) & "_" & toHex(hexsecs shr 4, 2) & "_" & toHex(hexsecs, 1)
    replace(turn, clock.ds, clock.timeHandle, TonalTime(num: hexsecs, hex: hexStr))

proc loop(clock: Clock) {.asyncio.} =
  let fd = newHexSecTimer()
  while true:
    update(clock)
    wait(FD fd, Read)
    var n: uint64
    discard read(fd, addr n, sizeof n)

runActor("tonaltime") do (turn: var Turn):
  resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
    during(turn, ds, ?:Args) do (ds: Cap, lat: float, lon: float):
      let
        horizon = horizonTimes(lon, lat, now())
        dayDur = horizon.sundown - horizon.sunrise
        noon = horizon.sunrise + (dayDur div 2)
        midnight = noon - initDuration(hours = 12)

      discard publish(turn, ds, Sunrise(s: $horizon.sunrise))
      discard publish(turn, ds, Sunset(s: $horizon.sundown))
      discard publish(turn, ds, Noon(s: $noon))
      discard publish(turn, ds, Midnight(s: $midnight))
        # TODO: recalculate every 12 hours

      discard trampoline:
        whelp Clock(facet: turn.facet, ds: ds, midnight: midnight).loop()

M tonaltime.nimble => tonaltime.nimble +2 -7
@@ 1,13 1,8 @@
# Package

version = "20231117"
version = "20240408"
author        = "Emery Hemingway"
description   = "Utility to print the current Tonal or hexadecimal time"
license       = "Unlicense"
srcDir        = "src"
bin           = @["tonaltime"]


# Dependencies

requires "nim >= 1.6.4", "syndicate"
requires "http://git.syndicate-lang.org/ehmry/syndicate-nim.git >= 20240405"