Skip to content

Websocket appendix

.mon.getMemWS

{[dict]
    dict:@[dict; `start`end; {"p"$"Z"$x}];
    dict[`syms]:except[; `] `$csv vs dict`syms;
    .qr.client.sendRequest[(`.monPublic.getMem; dict); `; {[h; res] neg[h] -8!.j.j .sd.r:res }[.z.w]; ()]
 }

getMemWS.html

<html>
  <head>
    <style>
      #table {
        font-family: Arial, Helvetica, sans-serif;
        border-collapse: collapse;
        width: 50%;
      }

      #table td, #table th {
        border: 1px solid #ddd;
        padding: 8px;
      }

      #table tr:nth-child(even){background-color: #f2f2f2;}

      #table th {
        padding-top: 20px;
        padding-bottom: 20px;
        text-align: left;
        background-color: rgb(82, 187, 248);
        color: white;
      }
    </style>
  </head>
  <body>
    <title>Websocket client</title>
    <meta charset="UTF-8">

    <form action="">
      <body>
      Start: <input type="date" id="start">
      End: <input type="date" id="end">
      Syms: <input type="text" id="syms" placeholder="server_A,server_B">
      <input type="submit" value="submit" onclick="return send()">
    </form>

    <div id="tableDiv" style="margin-top: 40px">

    </div>  

    <script src="c.js"></script>
    <script>
      var ws, cmd = "", c = connect();
      var output = document.getElementById("tableDiv");
      // get current & yesterday's dates
      var d = new Date();
      var d1 = new Date();
      d1.setDate(d1.getDate()-1);
      document.getElementById('start').valueAsDate = d1;
      document.getElementById('end').valueAsDate = d;

      function connect(){
        // this is where we define the WebSocket API functionality
        if ("WebSocket" in window) {
          ws = new WebSocket("ws://coredev1:35741/");
          ws.binaryType = 'arraybuffer';

          console.log("connecting...");

          ws.onopen=function(e){console.log("connected");};
          ws.onclose=function(e){console.log("disconnected");};

          ws.onmessage=function(e){
            d=deserialize(e.data);
            json = JSON.parse(d);
            // @todo check return type & error if not table
            // @todo deserialise table to JSON
            // @todo draw table & chart
            console.log("received\n" + d);
          }
          ws.onerror=function(e){console.log(e.data);}
        } else alert("WebSockets not supported on your browser.");
      }

      function send(){
        start = new Date(Date.parse(document.getElementById('start').value));
        end = new Date(Date.parse(document.getElementById('end').value));
        end.setDate(end.getDate() + 1)
        syms = document.getElementById('syms').value;
        dict = {start: start, end: end, syms: syms};

        args = ['.mon.getMemWS', dict]
        json = JSON.stringify(args);

        ws.send(s=serialize(json));
        console.log("sent " + json);

        ws.onmessage=function(e){
          des = deserialize(e.data);
          console.log(des);
          var t,d = JSON.parse(des);
          if (typeof d == "object") { // either a table or dictionary
            if (d.length) { // if d has a length it is a table
              // we will to iterate through the object wrapping it in the HTML table tags
              t = '<table id="table" border="1"><tr>'
              for (var x in d[0]) {
                // loop through the keys to create the table header
                t += '<th>' + x + '</th>';
              }
              t += '</tr>';
              for (var i = 0; i < 20; i++) {
                // loop through the rows, putting tags around each column value
                t += '<tr>';
                for (var x in d[0]) {
                  t += '<td>' + d[i][x] + '</td>';
                }
                t += '</tr>';
              }
              t += '</table>';
            } else {
              // if the object has no length, it must be a dictionary, we will iterate over the keys to print the key|value pairs as would be displayed in a q console
              t = "";
              for (var x in d) {
                t += x + " | " + d[x] + "<br />";
              }
            }
          } else {
            // if not an object, then the message must have a simple data structure, in which case we will just print it out.
            // @todo need to change the analytic to reverse if it's a table, not if anything else
            t = d;
          };
          output.innerHTML = cmd + t + "<br />" + output.innerHTML;
        }
        return false;
      }
    </script>
  </body>
</html>

c.js

c.js


// 2016.09.15 performance enhancement for temporal constructors and type identification
// 2016.03.18 char vectors and symbols now [de]serialize [from]to utf8
// 2014.03.18 Serialize date now adjusts for timezone.
// 2013.04.29 Dict decodes to map, except for keyed tables.
// 2013.02.13 Keyed tables were not being decoded correctly.
// 2012.06.20 Fix up browser compatibility. Strings starting with ` encode as symbol type.
// 2012.05.15 Provisional test release, subject to change
// for use with websockets and kdb+v3.0, (de)serializing kdb+ ipc formatted data within javascript within a browser.
// e.g. on kdb+ process, set .z.ws:{neg[.z.w] -8!value -9!x;}
// and then within javascript websocket.send(serialize("10+20"));
// ws.onmessage=function(e){var arrayBuffer=e.data;if(arrayBuffer){var v=deserialize(arrayBuffer);...
// note ws.binaryType = 'arraybuffer';

function u8u16(u16){
  var u8=[];
  for(var i=0;i<u16.length;i++){
    var c=u16.charCodeAt(i);
    if(c<0x80)u8.push(c);
    else if(c<0x800)u8.push(0xc0|(c>>6),0x80|(c&0x3f));
    else if(c<0xd800||c>=0xe000)u8.push(0xe0|(c>>12),0x80|((c>>6)&0x3f),0x80|(c&0x3f));
    else{
      c=0x10000+(((c&0x3ff)<<10)|(u16.charCodeAt(++i)&0x3ff));
      u8.push(0xf0|(c>>18),0x80|((c>>12)&0x3f),0x80|((c>>6)&0x3f),0x80|(c&0x3f));
    }
  }
  return u8;
}

function u16u8(u8){
  var u16="",c,c1,c2;
  for(var i=0;i<u8.length;i++)
    switch((c=u8[i])>>4){ 
      case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u16+=String.fromCharCode(c);break;
      case 12:case 13:c1=u8[++i];u16+=String.fromCharCode(((c&0x1F)<<6)|(c1&0x3F));break;
      case 14:c1=u8[++i];c2=u8[++i];u16+=String.fromCharCode(((c&0x0F)<<12)|((c1&0x3F)<<6)|((c2&0x3F)<<0));break;
    }
  return u16;
}

function deserialize(x){
  var a=x[0],pos=8,j2p32=Math.pow(2,32),ub=new Uint8Array(x),sb=new Int8Array(x),bb=new Uint8Array(8),hb=new Int16Array(bb.buffer),ib=new Int32Array(bb.buffer),eb=new Float32Array(bb.buffer),fb=new Float64Array(bb.buffer);
  const msDay=86400000, QEpoch = msDay*10957;
  function rBool(){return rInt8()==1;}
  function rChar(){return String.fromCharCode(rInt8());}
  function rInt8(){return sb[pos++];}
  function rNUInt8(n){for(var i=0;i<n;i++)bb[i]=ub[pos++];}
  function rUInt8(){return ub[pos++];}
  function rGuid(){var x="0123456789abcdef",s="";for(var i=0;i<16;i++){var c=rUInt8();s+=i==4||i==6||i==8||i==10?"-":"";s+=x[c>>4];s+=x[c&15];}return s;}
  function rInt16(){rNUInt8(2);var h=hb[0];return h==-32768?NaN:h==-32767?-Infinity:h==32767?Infinity:h;}
  function rInt32(){rNUInt8(4);var i=ib[0];return i==-2147483648?NaN:i==-2147483647?-Infinity:i==2147483647?Infinity:i;}
  function rInt64(){rNUInt8(8);var x=ib[1],y=ib[0];return x*j2p32+(y>=0?y:j2p32+y);}// closest number to 64 bit int...
  function rFloat32(){rNUInt8(4);return eb[0];}
  function rFloat64(){rNUInt8(8);return fb[0];}
  function rSymbol(){var i=pos,c,s=[];while((c=rUInt8())!==0)s.push(c);return u16u8(s);};
  function rTimestamp(){return date(rInt64()/1000000);}
  function rMonth(){return new Date(Date.UTC(2000,rInt32(),1));}
  function date(n){return new Date(QEpoch+n);}
  function rDate(){return date(rInt32()*msDay);}
  function rDateTime(){return date(rFloat64()*msDay);}
  function rTimespan(){return date(rInt64()/1000000);}
  function rSecond(){return date(rInt32()/1000);}
  function rMinute(){return date(rInt32()*60000);}
  function rTime(){return date(rInt32());}
  function r(){
    var fns=[r,rBool,rGuid,null,rUInt8,rInt16,rInt32,rInt64,rFloat32,rFloat64,rChar,rSymbol,rTimestamp,rMonth,rDate,rDateTime,rTimespan,rMinute,rSecond,rTime];
    var i=0,n,t=rInt8();
    if(t<0&&t>-20)return fns[-t]();
    if(t>99){
      if(t==100){rSymbol();return r();}
      if(t<104)return rInt8()===0&&t==101?null:"func";
      if(t>105)r();
      else for(n=rInt32();i<n;i++)r();
      return"func";}
    if(99==t){
      var flip=98==ub[pos],x=r(),y=r(),o;
      if(!flip){
        o={};
        for(var i=0;i<x.length;i++)
          o[x[i]]=y[i];
      }else
        o=new Array(2),o[0]=x,o[1]=y;
      return o;
    }
    pos++;
    if(98==t){
 //    return r(); // better as array of dicts?
      rInt8(); // check type is 99 here
    // read the arrays and then flip them into an array of dicts
      var x=r(),y=r();
      var A=new Array(y[0].length);
      for(var j=0;j<y[0].length;j++){
        var o={};
        for(var i=0;i<x.length;i++)
          o[x[i]]=y[i][j];
        A[j]=o;}
      return A;}
    n=rInt32();
    if(10==t){var s=[];n+=pos;for(;pos<n;s.push(rUInt8()));return u16u8(s);}
    var A=new Array(n);
    var f=fns[t];
    for(i=0;i<n;i++)A[i]=f();
    return A;}
  return r();}

function serialize(x){var a=1,pos=0,ub,bb=new Uint8Array(8),ib=new Int32Array(bb.buffer),fb=new Float64Array(bb.buffer);
  function toType(obj) {var jsType=typeof obj;if(jsType!=='object'&&jsType!=='function') return jsType;
    if(!obj)return 'null';if(obj instanceof Array)return 'array';if(obj instanceof Date)return 'date';return 'object';}
  function getKeys(x){return Object.keys(x);}
  function getVals(x){var v=[];for(var o in x)v.push(x[o]);return v;}
  function calcN(x,dt){
    var t=dt?dt:toType(x);
    switch(t){
      case'null':return 2;
      case'object':return 1+calcN(getKeys(x),'symbols')+calcN(getVals(x),null);
      case'boolean':return 2;
      case'number':return 9;
      case'array':{var n=6;for(var i=0;i<x.length;i++)n+=calcN(x[i],null);return n;}
      case'symbols':{var n=6;for(var i=0;i<x.length;i++)n+=calcN(x[i],'symbol');return n;}
      case'string':return u8u16(x).length+(x[0]=='`'?1:6);
      case'date':return 9;
      case'symbol':return 2+u8u16(x).length;}
    throw "bad type "+t;}
  function wb(b){ub[pos++]=b;}
  function wn(n){for(var i=0;i<n;i++)ub[pos++]=bb[i];}
  function w(x,dt){
    var t=dt?dt:toType(x);
    switch(t){
      case 'null':{wb(101);wb(0);}break;
      case 'boolean':{wb(-1);wb(x?1:0);}break;
      case 'number':{wb(-9);fb[0]=x;wn(8);}break;
      case 'date':{wb(-15);fb[0]=((x.getTime()-(new Date(x)).getTimezoneOffset()*60000)/86400000)-10957;wn(8);}break;
      case 'symbol':{wb(-11);x=u8u16(x);for(var i=0;i<x.length;i++)wb(x[i]);wb(0);}break;
      case 'string':if(x[0]=='`'){w(x.substr(1),'symbol');}else{wb(10);wb(0);x=u8u16(x);ib[0]=x.length;wn(4);for(var i=0;i<x.length;i++)wb(x[i]);}break;
      case 'object':{wb(99);w(getKeys(x),'symbols');w(getVals(x),null);}break;
      case 'array':{wb(0);wb(0);ib[0]=x.length;wn(4);for(var i=0;i<x.length;i++)w(x[i],null);}break;
      case 'symbols':{wb(0);wb(0);ib[0]=x.length;wn(4);for(var i=0;i<x.length;i++)w(x[i],'symbol');}break;}}
  var n=calcN(x,null);
  var ab=new ArrayBuffer(8+n);
  ub=new Uint8Array(ab);
  wb(1);wb(0);wb(0);wb(0);ib[0]=ub.length;wn(4);w(x,null);
  return ab;}