Toteutin 8th ohjelmointikielellä pörssärin JSON-datan koodaamisen bitteinä ja purkamisen takaisin kytkentäajoiksi. Oli kohtuullisen "viihdyttävä" tehtävä, kun piti huomioda aikavyöhyke ja mahdolliset kellojen siirtelyt. Lisäksi Pörssäri antaa vain tilamuutokset ja tarvittavat puuttuvat tilamuutokset vuorokauden vaihteessa piti muodostaa ja lisätä itse. Onneksi sentään 8th laskee isoilla luvuilla suoraan.
Käyttääkö kukaan muu bittikoodausta?
Testiohjelma tulostaa:
Käyttääkö kukaan muu bittikoodausta?
Koodi:
needs stack/rstack
needs date/utils
needs date/daylight
: zerotime \ dst-zone d -- d
"%Y-%M-%DT00:00:00" swap d:format d:parse tuck d:dst-ofs d:tzadjust ;
: dayhours? \ dst-zone d -- n
zerotime dup>r d:year@ 10 31 d:ymd> d:Sun d:prev-dow d:= if
25
else
r@ d:year@ 03 31 d:ymd> d:Sun d:prev-dow d:= if
23
else
24
then
then rdrop ;
: unix>n0 \ n dst-zone -- n
dup>r swap d:unix> dup d:>msec "%Y-%M-%DT00:00:00" rot d:format d:parse _swap tuck d:dst-ofs d:tzadjust
tuck d:>msec n:- 1000 n:/ n:int
60 n:/mod 15 n:/mod -rot 60 n:* n:+
810 n:< !if n:1+ then r> rot dayhours? 2 n:shl n:mod ;
: unix>n \ n dst-zone -- n
swap d:unix> dup d:>msec "%Y-%M-%DT00:00:00" rot d:format d:parse _swap tuck d:dst-ofs d:tzadjust
d:>msec n:- 1000 n:/ n:int
60 n:/mod 15 n:/mod -rot 60 n:* n:+
810 n:< !if n:1+ then ;
: n>d \ n dst-zone d -- d
repeat
2dup d:dst-ofs d:tzadjust 2dup dayhours? 2 n:shl dup>r 3 roll swap n:/mod
dup if
dup n:1- r> n:* rot n:+ swap
>r swap d:>ymd r> if
n:1+
then
3 a:close "%04d-%02d-%02dT00:00:00" s:strfmt d:parse
else
rdrop swap rot d:>ymd 3 roll 15 n:* 60 n:/mod swap 0 6 a:close d:join
then
_swap 2 pick
while! 2nip ;
: sequential-bits? \ a -- a
0 >r a:new swap
repeat
a:len n:1- r@ n:> !if
rdrop break
else
r@ n:1+ a:@ n:1- swap r@ a:@ rot n:= !if
r> n:1+ a:/ _swap a:push swap
0 >r
else
1 n:r+
then
then
again a:push ;
: msb? \ n -- msb
n:ln 2 n:ln n:/ n:int ;
: decode \ n -- a
a:new
( 2 pick 1 n:band if
a:push
else
drop
then
swap 1 bint n:shr swap
) 0 3 pick msb? loop nip ;
: encode \ a -- n
0 bint swap ( 1 bint swap n:shl n:bor ) a:each! drop ;
: is-bit-set? \ n n -- T
1 bint swap n:shl n:band ;
: set-bit \ n n -- n
1 bint swap n:shl n:bor ;
: remove-bit \ n n -- n
1 bint swap n:shl n:bnot n:band ;
: pörssari> \ m dst-zone -- a
>r "metadata" m:@ "timestamp" m:_@ swap "controls" m:_@ a:len !if
2drop rdrop null ;;
then
( "state" m:@ >n swap "schedules" m:_@ swap dup if
2 a:close [ "timestamp", "state" ] swap m:zip a:push
else
drop
then
( "timestamp" m:_@ >n d:unix> r@ swap zerotime d:>unix ) a:group
( nip ( "timestamp" m:_@ >n swap "timestamp" m:_@ >n n:cmp n:neg ) a:sort ) m:map
m:keys ( >n swap >n n:cmp n:neg ) a:sort
( m:@ 0 a:@ "state" m:@ >n !if
"timestamp" m:_@ >n d:unix> r@ swap zerotime
d:>unix 1 2 a:close [ "timestamp", "state" ] swap m:zip a:slide
else
drop
then
-1 a:@ "state" m:@ >n if
"timestamp" m:_@ >n d:unix> r@ swap tuck dayhours?
2 n:shl r@ rot n>d d:>unix 0 2 a:close [ "timestamp", "state" ] swap m:zip a:push
else
drop
then
drop ) a:each! drop
( nip ( "timestamp" m:@ >n r@ 3 roll if unix>n else unix>n0 then "timestamp" m:_! drop ) a:each ) m:map
( nip ( [1,0] a:_@ a:open "state" m:@ >n if
"timestamp" m:_@ swap ["timestamp", "state"] m:_@ a:open !if n:1- then ' noop -rot a:generate
else
2drop a:new
then ) 2 1 a:map+ a:squash encode ) m:map
m:keys ( >n swap >n n:cmp n:neg ) a:sort 0 >r
( tuck m:@ r@ n:shl 2 pick >n d:unix> 1 rpick swap dayhours? 2 n:shl n:r+ rot m:_! ) a:each! drop
m:vals nip ' n:bor 0 a:reduce rdrop ) a:map nip rdrop ;
: >d \ n dst-zone d -- a
rot decode sequential-bits? ( [0,-1] a:_@ 1 ' n:1+ a:op! ( 2 pick 2 pick n>d ) a:map ) a:map 2nip ;
\ Sample data
{
"metadata": {
"mac": "XXXXXXXXXXXX",
"channels": "1",
"fetch_url": "https://api.porssari.fi/getcontrols.php",
"timestamp": "1741798048",
"timestamp_offset": "7200",
"valid_until": "1741902300"
},
"controls": [
{
"id": "1",
"name": "LVV",
"updated": "1740313870",
"state": "0",
"schedules": [
{
"timestamp": "1741827683",
"state": "1"
},
{
"timestamp": "1741838460",
"state": "0"
}
]
}
]
} constant data
: app:main
\ Parse and encode time periods as bits (bint):
data "Europe/Helsinki" pörssari> dup . cr cr
\ Decode and display time periods for the first channel:
0 a:_@ "Europe/Helsinki" data "$.metadata.timestamp" json@ drop nip >n d:unix>
>d ( "%s - %s\n" s:strfmt . ) a:each! drop ;
Testiohjelma tulostaa:
Koodi:
[16773120]
2025-03-12T03:00:00+02:00 - 2025-03-12T06:00:00+02:00