///
/// @file proto.c
/// @authors k. Headley
/// @date 25 jan 2019

/// Module for prototype code
/// for convenient inclusion/exclusion using WITH_PROTO

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2019 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

/////////////////////////
// Headers
/////////////////////////
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "proto.h"

#include "rtc.h"
#include "timers.h"
#include "ioserial.h"
#include "iodigital.h"
#include "iowfile.h"
#include "licor8xx.h"
#include "licor8xx-shared.h"
#include "cwg-shared.h"
#include "ui.h"
#include "log.h"
#include "z85.h"
#include "sequence.h"
#include "schedule.h"
#include "script.h"
#include "micron.h"
#include "i2cp.h"
#include "sht3x.h"
#include "libpic30.h"

#ifdef WITH_PROTO

/////////////////////////
// Macros
/////////////////////////

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "carbonwg"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-2019 MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

/////////////////////////
// Declarations
/////////////////////////

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////

#ifdef PROTO_PROGVARS
// these should be global scope, so they can be stored in program space
// this works, but will corrupt the bootloader (fails on readback); indicates address x00102
// update: must use the exact limits defined in boot_config.h when programming 0x1800-oxab7fe
// and of course, the address must be less inside that range
//static  __prog__  int __attribute__ ((space(prog),address(0x2d000))) SerialNumber = 0xCAFE;
//static  __prog__  int __attribute__ ((space(prog),address(0x2000))) SerialNumber = 0xB0CA;
typedef struct sysinfo_s{
    int sn;
    int foo;
    int bar;
}sysinfo_t;

static  __prog__  sysinfo_t __attribute__ ((space(prog),address(0x2000))) sysinfo = {0xBEEF,0xB00F,0xCA80};

//unsigned  int SerialNumber=0xBEE5;
#endif //PROTO_PROGVARS


/////////////////////////
// Function Definitions
/////////////////////////
#ifdef PROTO_SCHEDULE

int proto_test_schedule()
{

    schedule_t s={0};
    // fields: m(1-12) md(1-31) h(0-23) m(0-59) s(0-59) wd(0-6, sun=0)
    // selectors: n, *, n-m, */n, i-j/n
    char spec[64]={0};
//    sprintf(spec,"* * * */5 * *");
//    sprintf(spec,"1,3,5");
//    sched_parse(s,spec);
//    sprintf(spec,"9-12");
//    sched_parse(s,spec);
//    sprintf(spec,"7");
//    sched_parse(s,spec);
//    sprintf(spec,"*/4");
//    sched_parse(s,spec);
//    sprintf(spec,"*/3");
//    sched_parse(s,spec);
//    sprintf(spec,"3-11/3");
//    sched_parse(s,spec);
//    sprintf(spec,"* 1,15,22");
//    sched_parse(s,spec);
//    sprintf(spec,"* 3-27");
//    sched_parse(s,spec);
//    sprintf(spec,"* 1,2,20-31/5");
//    sched_parse(s,spec);
//    sprintf(spec,"1 3 0,3 13-23/2 *");
//    sched_parse(s,spec);
    sprintf(spec,"1 2 3-4 */5 1,50-55,20-30/3 MO-TH");
    SCH_ZERO(s);
    sched_b_show(s,5);
    sched_parse(s,spec);
    sched_r_show(s,5);
    sched_b_show(s,5);

    sprintf(spec,"MAR-OCT 1 1 1 1 SU-SA/2");
    SCH_ZERO(s);
    sched_parse(s,spec);
    sched_r_show(s,5);
    sched_b_show(s,5);

     SCH_ZERO(s);
//    hex_show(s,24,10,5,true);
    sprintf(spec,"* * * * */10 *");
    sched_parse(s,spec);
    sched_r_show(s,5);
    sched_b_show(s,5);
//    hex_show(s,24,10,5,true);
#ifdef PROTO_SCHEDULE_DEMO
    OUT_CTTY("\r\nTesting match (every 10s)\r\n");
    OUT_CTTY("Press any key to quit\r\n");
    bool quit=false;
    Time tm,*ptm=&tm;
    while(!quit){

        // check for user input
        if(iow_get_char(false)!=0)
            break;

        // read RTC
        rtc_mktime(ptm);
        // check for schedule match
        char match=sched_match_tm(s,ptm)?'Y':'N';

        // test sched_match_tt
        // (wrapper for sched_match_tm, using time_t)
//        time_t tt=rtc_mktime(ptm);
//        char match=sched_match_tt(s,&tt)?'Y':'N';

        // update time display and match status
        // erase current value w/ BCKSP
        int i=0;
        for(i=0;i<24;i++)OUT_CTTY("\b");

        // print ISO8601 time string
        char tstr[24]={0};
        TM2ISO8601(ptm,tstr);
        OUT_CTTY("%s - %c",tstr,match);
        // delay 1 s
        tkDelayMs(1000);
    }
    OUT_CTTY("\r\n");
#endif
    return 0;
}

#endif

#ifdef PROTO_CRON

int proto_test_cron()
{
    cron_instance_t *cron=micron_get_instance(CRON_HND_DFL);

    micron_show(cron,5);
#if 0
    micron_tab_list(CRON_HND_DFL,-1);

    // "+|-"                  : enable/disable cron
    // "C:USR|SYS|*,+/-E+/-X" : configure tab
    // "L:USR|SYS|*"          : list tab(s))
    // "S:USR|SYS|*"          : tab status)
    // "F:USR|SYS,<path>"     : set tab file

    OUT_CTTY("\r\ntest - cli_cron LFC\r\n");
    char *argv[4]={"L:*","F:USR,FOO.TAB","C:USR,-E"};
    cli_cron(3,argv);
    micron_show(cron,5);

    OUT_CTTY("\r\ntest - cli_cron C\r\n");
    argv[0]="C:SYS,-E";
    cli_cron(1,argv);
    micron_show(cron,5);

    OUT_CTTY("\r\ntest - cli_cron +CFL\r\n");
    argv[0]="+C:SYS,+E";
    argv[1]="F:USR,CRONTAB.USR";
    argv[2]="L:USR";
    cli_cron(3,argv);
    micron_show(cron,5);

    OUT_CTTY("\r\ntest - cli_cron S:*\r\n");
    argv[0]="S:*";
    cli_cron(1,argv);
    OUT_CTTY("\r\ntest - cli_cron S:CRON\r\n");
    argv[0]="S:CRON";
    cli_cron(1,argv);

    OUT_CTTY("\r\n>>> micron_init\r\n");
    micron_init(cron);

    cron_tab_t *sys=micron_tab_slookup(CRON_HND_DFL,SYS_CRONTAB_STR);
    cron_tab_t *usr=micron_tab_slookup(CRON_HND_DFL,USR_CRONTAB_STR);
    micron_tab_set_flags(sys,TAB_MODE_EXCL|TAB_CTRL_EN);
    micron_tab_set_flags(usr,TAB_CTRL_EN);
    micron_tab_set_path(sys,SYS_CRONTAB_DFL,true);
    micron_set_enable(CRON_HND_DFL,true);

    OUT_CTTY("\r\ntest - cli_cron T\r\n");
    argv[0]="T:SYS,0,\"* * * * */3 *\"";
    cli_cron(1,argv);
    sched_r_show(cron->tabs[TABID_SYS].tasks[0].sch,5);
    sched_r_show(micron_tab_get_schedule(sys,0),5);

    OUT_CTTY("\r\ntest - cli_cron T\r\n");
    argv[0]="T:SYS,0,\"* * 6-18 */20 * SU-SA/2\"";
    cli_cron(1,argv);
    sched_r_show(cron->tabs[TABID_SYS].tasks[0].sch,5);
    sched_r_show(micron_tab_get_schedule(sys,0),5);

    schedule_t sch={0};
    char x[16]="* * * * */3 *";
    sched_parse(sch,x);
    micron_tab_set_task(sys,0,sch,NULL);
//    sched_r_show(sch,5);
//    sched_r_show(cron->tabs[TABID_SYS].tasks[0].sch,5);

    OUT_CTTY("\r\n>>> initialized\r\n");
    micron_show(cron,5);

    int i=0;
    for(i=0;i<10;i++){
        OUT_CTTY("\r\n+++ iteration[%d/10]\r\n",i+1);
        // update script state machine
        script_state_update();
        // update cron state machine
        micron_state_update(CRON_HND_DFL);
        //get & do user command
        ui_console_update();
        tkDelayMs(500);
    }
    //micron_run(CRON_HND_DFL);
#endif

    // test schedule next
    schedule_t sch={0};
//    const char *foo[3]={"* * * */10 */20 *","* */5 * */5 */20 *","* * * */5 */20 MO-WE"};
    char x[16]={0};
    char tstr[32]={0};

    Time tm={0},*ptm=&tm;
    rtc_mktime(ptm);

//    int i=0;
//    for(i=0;i<3;i++){
//        sprintf(x,"%s",foo[i]);
//        SCH_ZERO(sch);
//        sched_parse(sch,x);
//        time_t next=sched_next(sch,ptm);
//        TM2ISO8601(ptm, tstr);
//        OUT_CTTY("sch[%s] wd[%d] tm[%s] next[%ld]\r\n",foo[i],ptm->tm_wday,tstr,next);
//        sched_r_show(sch,5);
//    }


//    while(1){
////        const char *ss="* * */2 */3 0,59 *";
//        const char *ss="* * */2 */5 0 *";
//        sprintf(x,"%s",ss);
//        SCH_ZERO(sch);
//        sched_parse(sch,x);
//        int c=0;
//        memset(ptm,0,sizeof(Time));
//        rtc_mktime(ptm);
//        time_t next=sched_next(sch,ptm);
//        TM2ISO8601(ptm, tstr);
//        OUT_CTTY("tm[%s] next[%ld]\r\n\r\n",tstr,next);
////        OUT_CTTY("sch[%s] wd[%d] tm[%s] next[%ld]\r\n",ss,ptm->tm_wday,tstr,next);
////        sched_r_show(sch,5);
////        sched_b_show(sch,5);
//        if((c=iow_get_char(false))!=0){
//            break;
//        }
//        tkDelayMs(500);
//    }

    const char *ss="* 2-31/2 0-6/2 */10 15 -";
    rtc_mktime(ptm);
//    ptm->tm_mday=28;
    ptm->tm_hour=22;
    ptm->tm_min=35;
    ptm->tm_sec=0;

    OUT_CTTY("sch[%s]\r\n",ss);
    sprintf(x,"%s",ss);
    SCH_ZERO(sch);
    sched_parse(sch,x);
    sched_r_show(sch,5);
    sched_b_show(sch,5);

    while(1){
        sprintf(x,"%s",ss);
        SCH_ZERO(sch);
        sched_parse(sch,x);
        int c=0;
        OUT_CTTY("sch[%s]\r\n\r\n",ss);
        time_t next=sched_next(sch,ptm);
        TM2ISO8601(ptm, tstr);
        OUT_CTTY("next[%ld]\r\n\r\n\r\n",next);
        if((c=iow_get_char(true))!=0){
            if(c=='Q')
            break;
        }
        switch(c){
            case 'H':
                ptm->tm_hour++;
                break;
            case 'M':
                ptm->tm_min++;
                break;
            case 'D':
                ptm->tm_mday++;
                break;
            default:
                ptm->tm_sec++;
                break;
        }

        if(ptm->tm_sec>=60){
            ptm->tm_sec=0;
            ptm->tm_min++;
        }
        if(ptm->tm_min>=60){
            ptm->tm_min=0;
            ptm->tm_hour++;
        }
        if(ptm->tm_hour>=24){
            ptm->tm_hour=0;
            ptm->tm_mday++;
            ptm->tm_wday++;
            ptm->tm_yday++;
        }
        if(ptm->tm_wday>=7){
            ptm->tm_wday=0;
        }
        switch(ptm->tm_mon){
            case 0://jan
            case 2://mar
            case 4://may
            case 6://jul
            case 7://aug
            case 9://oct
            case 11://dec
                if(ptm->tm_mday>31){
                    ptm->tm_mday=1;
                    ptm->tm_mon++;
                }
                break;
            case 1://feb
                if(SCH_IS_LEAPYEAR(ptm->tm_year+1900)){
                    if(ptm->tm_mday>29){
                        ptm->tm_mday=1;
                        ptm->tm_mon++;
                    }
                }else{
                    if(ptm->tm_mday>28){
                        ptm->tm_mday=1;
                        ptm->tm_mon++;
                    }
                }
                break;
            case 3://apr
            case 5://jun
            case 8://sep
            case 10://sep
                if(ptm->tm_mday>30){
                    ptm->tm_mday=1;
                    ptm->tm_mon++;
                }
                break;
            default:
                //ERROR
                TRACE();
                break;
        }
        if(ptm->tm_mon>=12){
            ptm->tm_mon=0;
            ptm->tm_year++;
        }
//        tkDelayMs(500);
    }
    return 0;
}
#endif

#ifdef PROTO_TOKENIZE
int tokenize(char *src, char **dest, char *del, int ntok){

    int i=0;
    while(i<ntok){
        dest[i]=strtok((i==0?src:NULL),del);
        if(NULL==dest[i]){
            break;
        }
        i++;
    }
    return i;
}

#endif // PROTO_TOKENIZE

#ifdef PROTO_TASK
#include "phex.h"
#include "zrec.h"
#define NAME_TABLE_BYTES 1024
char g_name_table[NAME_TABLE_BYTES]={0};
char *g_ip_name=g_name_table;
#define TABLE_SPACE(table,sz,cursor) (table+sz-cursor)
#define STRING_BYTES(s) (strlen(s)+1)

char *lookup_string(char *table, size_t len, char *str){
    char *pt=table;
    if(NULL!=table && NULL!=str){
    while(pt<(table+len)){
        if(strcmp(pt,str)==0){
            return pt;
        }
        pt+=STRING_BYTES(pt);
    }
    }
    return NULL;
}

int register_string(char *src, char *table, size_t sz, char **pt, char **pout){
    size_t slen = STRING_BYTES(src);
    int retval = ERR_SUCCESS;
    char *tp=lookup_string(table,sz,src);
    if(NULL!=tp && NULL!=pout){
        *pout = tp;
    }else if(NULL!=src && NULL!=table && NULL!=pout){
        if(TABLE_SPACE(table,sz,*pt) > slen){
            memcpy(table,src,slen);
            *pout = *pt;
            *pt+=slen;
        }
    }else{
        retval=ERR_FAILURE;
    }
    return retval;
}

//typedef struct state_s{
//    char *name;
//    byte iomask[4];
//}state_t;
#pragma pack(push,1)
typedef struct operation_s{
    char *name;
//    state_t *state;
    // bytes 0:2 - IO bit mask
    // bytes 3:3 - op ID
    byte state[4];
    uint32_t duration;

}operation_t;
#pragma pack(pop)

typedef struct cycle_s{
    char *name;
    uint8_t op_count;
    operation_t *op;
    uint16_t count;
}cycle_t;


//state_t state_table[64];
//operation_t op_table[64];
//cycle_t cycle_table[128];
#define LINE_BUF_BYTES 128
int proto_test_task()
{
     int retval = ERR_FAILURE;

    FILE_T file;
    program_t src = {"TEST.SCR",&file};
    program_t *psrc = &src;

    char line[LINE_BUF_BYTES]={0};
    size_t line_number=1;
//    op_t op={0};
//    op_t *pop=&op;
       FOPEN(psrc);

    while(read_line(psrc,line, LINE_BUF_BYTES)>=0){
        uiParse(line,NULL);
//        memset(pop,0,sizeof(op));
//        if(parse_record(line, pop)==0){
//            show_op(pop,true,2);
//        }else{
//            fprintf(stderr,"parse error line[%lu] len[%lu] [%s]\n",(long unsigned)line_number, (long unsigned)strlen(line),line);
//        }
        line_number++;
        memset(line,0,LINE_BUF_BYTES);
    }
    FCLOSE(psrc->fp);

    return retval;
}
#endif //PROTO_TASK

#ifdef PROTO_SCRIPT
#include "script.h"
#include "ioserial.h"
#include "ui.h"
#include "licor8xx.h"
#include "iodigital.h"

// 1kb ~ 6% data mem
//byte array[1024]={0};

int proto_test_script()
{
     int retval = ERR_FAILURE;

     //     doSetBit(XCVR_LICOR,1);
//     if((retval=licor_initialize(NULL))==ERR_SUCCESS){
//         OUT_CTTY("%s - licor init OK[%d]\r\n",__FUNCTION__,retval);
//     }else{
//         OUT_CTTY("%s - licor init ERR[%d]\r\n",__FUNCTION__,retval);
//     }
//
//     licor_raw_measurement(NULL,0,LMF_CONSOLE);
////    licor_set_strip(true);
////    licor_raw_measurement(NULL,0,LMF_CONSOLE);
//     licor_record_t rec;
//
//     char i='A';
//     for(i='A';i<'D';i++){
//        licor_parsed_measurement(&rec, i, LMF_CONSOLE|LMF_LOG);
//        tkDelayMs(100);
//     }
//
//     doSetBit(XCVR_LICOR,0);
////    char *src="TEST2.SCR";
////     if(script_load(src,true)!=NULL){
////       OUT_CTTY("proto_test running [%s]\r\n",src);
////        retval = script_run(src);
////        OUT_CTTY("proto_test ran [%s] ret[%d]\r\n",src,retval);
////     }
     return retval;
}
#endif // PROTO_SCRIPT
#ifdef PROTO_PALLOC
#include "cwgsys.h"
#include "ioserial.h"
#include "ui.h"

int proto_test_palloc()
{
    int retval = ERR_FAILURE;

    sys_init();
    pshow();
    char *pc = (char *)palloc(strlen("  \t\r\nhoody hoo\r\n")+1);
         sprintf(pc,"  \t\r\nhoody hoo\r\n");
         pshow();
         char *pd = pstrdup(pc);
         char *pt = ptstrdup(pc,true,true);
         pshow();
         pfree((void *)pc);
         pfree(pd);
         pfree(pt);
         pshow();

    char *px = (char *)palloc(128);
    if(NULL==px)
        EPRINT("palloc(%d) returned NULL",128);

    int i=0;
    char *p16[10]={0};
    char *p32[10]={0};
    for(i=0;i<10;i++){
        if( (p16[i] = (char *)palloc(10))==NULL)
            OUT_CTTY("p16[%d] NULL\r\n",i);
        else
            FILL_MEM(p16[i] ,'A'+i,10);
        if( (p32[i] = (char *)palloc(20))==NULL)
            OUT_CTTY("p32[%d] NULL\r\n",i);
        else
            FILL_MEM(p32[i] ,'0'+i,20);
    }
    pshow();
    for(i=0;i<10;i++){
        pfree(p16[i]);
        pfree(p32[i]);
    }
    pshow();

    return retval;
}
#endif // PROTO_PALLOC

#ifdef PROTO_CONFIG
#include "config.h"
#include "cwgsys.h"
#include "ioserial.h"
#include "ui.h"


int proto_test_config()
{
    SYS_SET_MDFLAGS(MOD_CFG);

    OUT_CTTY("config defaults:\r\n");
    config_show(NULL,true,5);
    OUT_CTTY("loading[%s]:\r\n",SYS_CONFIG_DFL);
    config_load(NULL);
    OUT_CTTY("config loaded:\r\n");
   config_show(NULL,true,5);
    bool echo=false;
    return_code_t test;
    OUT_CTTY("echo[%s]\r\n",(echo?"true":"false"));
    test=config_get(CK_ECHO,&echo);
    OUT_CTTY("config_get(%s) returned[%d/%s]\r\n",CK_ECHO,test,(echo?"true":"false"));
    echo=!echo;
    test=config_set(CK_ECHO, &echo);
    OUT_CTTY("config_set(%s/%s) returned[%d]\r\n",CK_ECHO,(echo?"true":"false"),test);
     test=config_get(CK_ECHO,&echo);
    OUT_CTTY("config_get(%s) returned[%d/%s]\r\n",CK_ECHO,test,(echo?"true":"false"));

    uint32_t u32=0xABACADBA;
    int32_t i32=76543210;
    config_set(CK_FOO_U32,&u32);
    config_set(CK_FOO_I32,&i32);

    char foo_s[64]={0};

     test=config_get(CK_FOO_S,foo_s);
    OUT_CTTY("config_get(%s) returned[%d/%s]\r\n",CK_FOO_S,test,foo_s);
    sprintf(foo_s,"a new foo");
    config_set(CK_FOO_S,&foo_s);
    OUT_CTTY("config_set(%s/%s) returned[%d]\r\n",CK_FOO_S,foo_s,test);

    config_show(NULL,true,5);
    config_export("CFGFOO.TXT");
   return 0;
}

#endif

 #ifdef PROTO_SIZES
   int proto_test_sizes()
   {
 //     Time stm;
//    Time *pt=&stm;
//    time_t tt;
//    char tstr[64];
//    char *ps;
//    int i=0;
//    for(i=0;i<30;i++)
//    {
//        rtcRead(&stm);
//        tt=mktime(&stm);
//        RTC_ISO8601(pt, tstr);
//        ps=tstr+strlen(tstr);
//        sprintf(ps," / %lu\r\n",(unsigned long)tt);
//        OUT_CTTY("%s",tstr);
//        tkDelayMs(1000ul);
//    }
        int w=10;
    OUT_CTTY("sizeof\r\n");
    OUT_CTTY("byte            %*zu\r\n",w,sizeof(byte));
    OUT_CTTY("float           %*zu\r\n",w,sizeof(float));
    OUT_CTTY("double          %*zu\r\n",w,sizeof(double));
    OUT_CTTY("long double     %*zu\r\n",w,sizeof(long double));
    OUT_CTTY("long long       %*zu\r\n",w,sizeof(long long int));
    OUT_CTTY("uint32_t        %*zu\r\n",w,sizeof(uint32_t));
    OUT_CTTY("long            %*zu\r\n",w,sizeof(long int));
    OUT_CTTY("int             %*zu\r\n",w,sizeof(int));
    OUT_CTTY("Time            %*zu\r\n",w,sizeof(Time));
    OUT_CTTY("licor_vrecord_t %*zu\r\n",w,sizeof(licor_vrecord_t));
    OUT_CTTY("licor_xrecord_t %*zu\r\n",w,sizeof(licor_xrecord_t));
    OUT_CTTY("licor_brecord_t %*zu\r\n",w,sizeof(licor_brecord_t));
    OUT_CTTY("bline_state_t   %*zu\r\n",w,sizeof(bline_state_t));
    OUT_CTTY("avg_state_t     %*zu\r\n",w,sizeof(avg_state_t));
    return 0;
   }
#endif

#ifdef PROTO_NMEAS
     int proto_test_nmeas()
     {
     double rate=0.5;
    uint16_t sample_n=5;
    // sample rate+10%
    uint32_t to_msec=1100*rate*sample_n;

    // override rate and sample count defaults
    if(NULL!=args){
        sscanf(args,"%u %lf ",&sample_n,&rate);
        to_msec=1100*rate*sample_n;
    }

    // 0:current 1: sum 2: avg 3:prev
    licor_mrecord_t records[3];
    avg_record_t hargs;
    bline_record_t bargs[2];
    bline_record_t *pb=NULL;

    ZERO_MEM(records, 3*sizeof(licor_mrecord_t) );
    ZERO_MEM(&hargs, sizeof(avg_record_t) );
    hargs.records=records;
    ZERO_MEM(&bargs, 2*sizeof(bline_record_t) );

    doSetBit(24,HI);
    OUT_CTTY("%s - rate[%.2lf] sample_n[%u] to_msec[%lu]\r\n",__FUNCTION__,rate,sample_n,to_msec);
    OUT_CTTY("%s - init licor\r\n",__FUNCTION__);
    licor_initialize(NULL);

    OUT_CTTY("%s - Taking pre-baseline (n=%d)\r\n",__FUNCTION__,sample_n/2);
    pb=&bargs[0];
    licor_stream_x(rate, sample_n/2, licor_bline_handler, pb, RT_BLINE, to_msec);

     OUT_CTTY("%s - Taking measurement\r\n",__FUNCTION__);
   licor_stream_x(rate, sample_n, licor_avg_handler, &hargs, RT_MEAS, to_msec);

    OUT_CTTY("%s - Taking post-baseline (n=%d)\r\n",__FUNCTION__,sample_n/2);
    pb=&bargs[1];
    licor_stream_x(rate, sample_n/2, licor_bline_handler, pb, RT_BLINE, to_msec);


    OUT_CTTY("\r\n");
    pb=&bargs[0];
    OUT_CTTY("%s - Pre-Baseline [N=%d] \r\n",__FUNCTION__,pb->n);
    OUT_CTTY("    End points a[%llu, %.3lf] b[%llu, %.3lf]\r\n", pb->timestamps[0],pb->epoints[0], pb->timestamps[1],pb->epoints[1]);
    OUT_CTTY("    Min[%.3lf] Max[%.3lf] Mean[%.3lf] Sdev[%.3lf]\r\n",
            BLINE_MIN(pb), BLINE_MAX(pb), BLINE_MEAN(pb), BLINE_SDEV(pb));


    OUT_CTTY("\r\n");
    OUT_CTTY("%s - Measurement: \r\n",__FUNCTION__);
    OUT_CTTY("    t_start[%llu] t_end[%llu] elapsed[%llu]\r\n",hargs.timestamps[0],hargs.timestamps[1],hargs.timestamps[1]-hargs.timestamps[0]);
//    OUT_CTTY("    SUM [N=%d]:\r\n",hargs.n);
//    licor_mrecord_show(&records[0], true, 5);
    OUT_CTTY("    AVG bytes[%d]\r\n",sizeof(licor_mrecord_t));
    licor_mrecord_show(&records[1], true, 5);
    OUT_CTTY("    PEAK H: %.3lf\r\n", hargs.peak);
    OUT_CTTY("    PEAK A: %.3lf\r\n", hargs.area);
    char *rec=(char *)palloc(LI_LOG_RECORD_BYTES);
    if(NULL!=rec){
        licor_record_str(rec,&records[1]);
        OUT_CTTY("    AVG log record len[%d]:\r\n",strlen(rec));
        licor_publish(rec, LMF_CONSOLE);
        pfree(rec);
    }else{
        EPRINT("%s - ERR - could not allocate record buffer",__FUNCTION__);
    }

    pb=&bargs[1];
    OUT_CTTY("\r\n");
    OUT_CTTY("%s - Post-Baseline [N=%d] \r\n",__FUNCTION__,pb->n);
    OUT_CTTY("    End points a[%llu, %.3lf] b[%llu, %.3lf]\r\n", pb->timestamps[0],pb->epoints[0], pb->timestamps[1],pb->epoints[1]);
    OUT_CTTY("    Min[%.3lf] Max[%.3lf] Mean[%.3lf] Sdev[%.3lf]\r\n",
            BLINE_MIN(pb), BLINE_MAX(pb), BLINE_MEAN(pb), BLINE_SDEV(pb));
  OUT_CTTY("\r\n");

    OUT_CTTY("bline_record_t 2@[%3u] [%4u]\r\n",sizeof(bline_record_t),2*sizeof(bline_record_t));
    OUT_CTTY("avg_record_t   1@[%3u] [%4u]\r\n",sizeof(avg_record_t),1*sizeof(avg_record_t));
    OUT_CTTY("licor_mrecord_t      3@[%3u] [%4u]\r\n",sizeof(licor_mrecord_t),3*sizeof(licor_mrecord_t));
    OUT_CTTY("total                        [%4u]\r\n",2*sizeof(bline_record_t)+sizeof(avg_record_t)+3*sizeof(licor_mrecord_t));
  OUT_CTTY("\r\n");
    return 0;
    }
#endif
#ifdef PROTO_QUEUE
    int proto_test_queue()
    {
         return 0;
 queue_entry_t qe={0};

log_queue_init();

char *str[]={
    "aaa","bbb","ccc",
    "ddd","eee","fff",
    "ggg","hhh","iii",
    "jjj","kkk","lll"};
int id[]={
    1, 2, 3,
    4, 5, 6,
    7, 8, 9,
    10,11,12};
int i=0;

OUT_CTTY("///// COUNT [empty]\r\n");
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());

OUT_CTTY("///// PEEK [empty]\r\n");
for(i=0;i<LOG_QUEUE_DEPTH;i++){
if(log_queue_peek(i, &qe)==ERR_SUCCESS)
    OUT_CTTY("queue peek[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
//    OUT_CTTY("queue peek[%d] OK\r\n",i);
else
    OUT_CTTY("peek failed\r\n");
}


OUT_CTTY("///// PUSH [5]\r\n");
for(i=0;i<5;i++){
log_queue_push((byte *)str[i], id[i], strlen(str[i])+1);
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());
}

OUT_CTTY("///// PEEK [5]\r\n");
for(i=0;i<5;i++){
if(log_queue_peek(i, &qe)==ERR_SUCCESS)
     OUT_CTTY("queue peek[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("peek failed\r\n");
}

OUT_CTTY("///// PUSH [+2]\r\n");
for(i=5;i<7;i++){
log_queue_push((byte *)str[i], id[i], strlen(str[i])+1);
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());
}

OUT_CTTY("///// POP [-5]\r\n");
for(i=0;i<5;i++){
if(log_queue_pop(&qe)==ERR_SUCCESS)
    OUT_CTTY("queue pop[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("pop failed\r\n");
}
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());

OUT_CTTY("///// PEEK [all - 2?]\r\n");
for(i=0;i<LOG_QUEUE_DEPTH;i++){
if(log_queue_peek(i, &qe)==ERR_SUCCESS)
    OUT_CTTY("queue peek[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("peek failed\r\n");
}
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());

log_queue_show();

OUT_CTTY("///// PUSH [+12]\r\n");
for(i=0;i<12;i++){
if(log_queue_push((byte *)str[i], id[i], strlen(str[i])+1)==ERR_SUCCESS){
OUT_CTTY("queue push[%d] [%s]\r\n",i,str[i]);
}else{
OUT_CTTY("queue push failed\r\n");
}
OUT_CTTY("queue count[%lu]\r\n",log_queue_count());
}
log_queue_show();

OUT_CTTY("///// PEEK [all - 10?]\r\n");
for(i=0;i<LOG_QUEUE_DEPTH;i++){
if(log_queue_peek(i, &qe)==ERR_SUCCESS)
    OUT_CTTY("queue peek[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("peek failed\r\n");
}
log_queue_show();
OUT_CTTY("///// POP [1 - oldest?]\r\n");
if(log_queue_pop(&qe)==ERR_SUCCESS)
    OUT_CTTY("queue pop {id[%02d] len[%03d] data[%s]}\r\n",qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("pop failed\r\n");

OUT_CTTY("///// POP [all+3]\r\n");
for(i=0;i<LOG_QUEUE_DEPTH+3;i++){
if(log_queue_pop(&qe)==ERR_SUCCESS)
    OUT_CTTY("queue pop[%02d] {id[%02d] len[%03d] data[%s]}\r\n",i,qe.id,qe.len,(qe.len>0?(char *)qe.data:"empty"));
else
    OUT_CTTY("pop failed\r\n");
}
return 0;
    }
#endif

#ifdef PROTO_UI_TOKENIZER
    int proto_test_ui_tokenizer()
    {
     // test  ui_tokenize
    if(NULL!=args){
        OUT_CTTY("src[%s]\r\n",args);
        char *tokens[5];
        int ntoks=ui_tokenize(args, tokens," ", 5);
        int i=0;
        OUT_CTTY("ntoks[%d]:\r\n",ntoks);
        for(i=0;i<ntoks;i++)
            OUT_CTTY("tok[%d] - [%s]\r\n",i,tokens[i]);
    //    pshow(NULL);
        OUT_CTTY("releasing tokens\r\n");
        ui_release_toks(tokens,ntoks);
    //    pshow(NULL);
    }
        return 0;
    }
#endif

#ifdef PROTO_BUG_STRUCT_ALIGN
    int proto_test_bug_struct_align()
    {
        // config_get fails into vrecord oflags
        // due to struct alignment issue

        #include "config.h"
        uint32_t flags=0x123;
        config_get(CK_LICOR_OFLAGS,&flags);
        OUT_CTTY("flags[%08lX]\r\n",flags);
        serTxWait(SER_CONS);
        licor_vrecord_t vr={0},*pr=&vr;
        vr.type=RT_REF;
        vr.oflags=0xabc;
        licor_vrecord_show(pr,true,5);
        serTxWait(SER_CONS);
        uint32_t *pf = &pr->oflags;
        OUT_CTTY("pf[%p/%08lX]\r\n",pf,*pf);
        serTxWait(SER_CONS);

        config_get(CK_LICOR_OFLAGS,pf);
        OUT_CTTY("pf[%p/%08lX]\r\n",pf,*pf);
        config_get(CK_LICOR_OFLAGS,&pr->oflags);
        OUT_CTTY("pr->oflags[%p/%08lX]\r\n",&pr->oflags,pr->oflags);
        return 0;
    }
#endif

#ifdef PROTO_FLAG_MNEM
    int proto_test_flag_mnem()
    {
         char cstr[LI_CFLAG_MNEM_BYTES]={0};
        uint32_t flags=0;
        int i=0;
        for(i=0;i<32;i++){
            ZERO_MEM(cstr,LI_CFLAG_MNEM_BYTES);
            flags |= (1<<i);
            licor_cflag2mnem(cstr,&flags,LI_CFLAG_MNEM_BYTES);
            OUT_CTTY("flags[%08lX] str[%s]\r\n",flags,cstr);
        }
        char ostr[LI_OFLAG_MNEM_BYTES]={0};
        flags=0;
        for(i=0;i<32;i++){
            ZERO_MEM(ostr,LI_OFLAG_MNEM_BYTES);
            flags |= (1ul<<i);
            licor_oflag2mnem(ostr,&flags,LI_OFLAG_MNEM_BYTES);
            OUT_CTTY("flags[%08lX] ostr[%s]\r\n",flags,ostr);
        }
         return 0;
    }
#endif
#ifdef PROTO_TRAP_TEST
    int proto_test_trap_test()
    {
        if(argc>0){
            switch (argv[0][0]){
                case 'S':
                INTCON1bits.STKERR = 1;
                    break;
                case 'A':
                INTCON1bits.ADDRERR = 1;
                    break;
                case 'O':
                INTCON1bits.OSCFAIL = 1;
                    break;
                case 'M':
                INTCON1bits.MATHERR = 1;
                    break;
                case '?':
                TRAPS_halt_on_error(4);
                    break;
                default:

                    break;
            }
        }
        return 0;
    }
#endif
#ifdef PROTO_HEXSHOW_TEST
    int proto_test_hexshow_test()
    {
        byte buf[50];
        int i=0;
        for(i=0;i<50;i++){
            buf[i]=(byte)i;
        };
        TRACE();
        hex_show(buf,50,16,5,true);
        TRACE();
        hex_show(buf,50,-1,3,true);
        TRACE();
        hex_show(buf,50,-1,-2,true);
         return 0;
    }
#endif
#ifdef PROTO_LESS_TEST
    int proto_test_less_test()
    {
        #include "iofile.h"
        if(argc>0){
            char *file = argv[0];
            int rows = (argc>1? atoi(argv[1]) : 25);
            fc_less(file,rows);
        }
         return 0;
    }
#endif
#ifdef PROTO_SEEK_TEST
    int proto_test_seek_test()
    {
        #if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
            FILE_T file_s, *fp=&file_s;
        #else
            FILE_T *fp;
        #endif
            uint32_t seek_t[2]={2},seek_ms=0;
            uint32_t wr_t[2]={2},wr_ms=0;
            size_t rb=0;
            byte data[1024]={0};
            memset(data,'*',1024);

            uint32_t len=0;
            iow_fseek(fp,0);
            len=iow_fsize(fp);
            int i=0;
            uint32_t lim=200;
            sscanf((argc>0?argv[0]:"-"),"%lu",&lim);
            while(seek_ms<lim){
                // TODO: use iow_file API
                if(iow_fopen(&fp,"SEEKTEST.DAT",(IOW_WRITE | IOW_OPEN_ALWAYS))==0){

                    wr_t[0]=tkGetTicks();
                    for(i=0;i<1024;i++){
                        len=FSIZE(fp);
                        iow_fseek(fp,len);
                        rb=iow_fwrite(fp,data,1024)
                    }
                    wr_t[1]=tkGetTicks();

                    len=iow_fsize(fp);
                    iow_fseek(fp,0);
                    seek_t[0]=tkGetTicks();
                    iow_fseek(fp,len);
                    seek_t[1]=tkGetTicks();

                    seek_ms = (seek_t[1]-seek_t[0])*MS_PER_TICK;
                    wr_ms = (wr_t[1]-wr_t[0])*MS_PER_TICK;
                    OUT_CTTY("len[%10lu] seek_ms[%4lu] wr_ms[%4lu]\r\n",len,seek_ms,wr_ms);
                    iow_fclose(fp);
                    if(serGetChar(SER_CONS))break;
                }
        }
         return 0;
    }
#endif
#ifdef PROTO_MAXSEG
    int proto_test_maxseg()
    {
#include "iofile.h"
        FILINFO finfo, *fp=&finfo;
        DIR dir,*pd=&dir;
        int i=0;
        while ( fc_dir_next((i==0?"/":NULL), &pd, &fp)!=NULL ){
            OUT_CTTY(" [%d] - %s",i++,fp->fname);
                char *x[2]={NULL,NULL};
            if( strstr(fp->fname,"RAW")!=NULL){
                x[0]=strtok(fp->fname,".");
                x[1]=strtok(NULL,".");
                if(NULL!=x[1]){
                    OUT_CTTY(" %10s\r\n",x[1]);
                }else{
                    OUT_CTTY("\r\n",x[1]);
                }
            }else{
                    OUT_CTTY("\r\n",x[1]);
                }
        }

        OUT_CTTY("maxseg.RAW[%lu]\r\n",log_max_seg(LOG_RAW));
        OUT_CTTY("maxseg.SYS[%lu]\r\n",log_max_seg(LOG_SYSLOG));
        OUT_CTTY("maxseg.DAT[%lu]\r\n",log_max_seg(LOG_DATA));
         return 0;
    }
#endif
#ifdef PROTO_LISTAT
    int proto_test_listat()
    {
        #define BUF_LEN (2048)
        char xml[BUF_LEN]={0};
        int rb = 0;
        if( (rb=licor_read_status(xml,BUF_LEN))>0 ){
            OUT_CTTY("licor_read_status ret[%d]\r\n",rb);
            licor_show_status(xml,true,5);
        }else{
            OUT_CTTY("licor_read_status failed [%d]\r\n",rb);
        }
        return 0;
    }
#endif
#ifdef PROTO_DATA_SIZES
    int proto_test_data_sizes()
    {
        OUT_CTTY(">> Data sizes\r\n");
        OUT_CTTY("licor_zrecord_t  [%zu]\r\n",sizeof(licor_zrecord_t));
        OUT_CTTY("mdata_Z_t        [%zu]\r\n",sizeof(mdata_Z_t));
        OUT_CTTY("mdata_Z_t*       [%zu]\r\n",sizeof(mdata_Z_t *));
        OUT_CTTY("log_rec_header_t [%zu]\r\n",sizeof(log_rec_header_t));
        OUT_CTTY("float            [%zu]\r\n",sizeof(float));
        OUT_CTTY("double           [%zu]\r\n",sizeof(double));
         return 0;
    }
#endif
#ifdef PROTO_Z85
    int proto_test_z85()
    {
        char *str="the quick brown fox jumps over the lazy dog 1.2.3...";
        size_t slen=strlen(str)+1;
        size_t esize=5*(slen)/4;
        char encoded[esize+1];
        size_t elen=slen+(4-slen%4);
        byte data[elen+1];
        OUT_CTTY("original:\"%s\"\r\n",str);
        OUT_CTTY("strlen(%zu) slen[%zu] esize[%zu] elen[%zu]\r\n",strlen(str),slen,esize,elen);

        ZERO_MEM(encoded,esize+1);
        ZERO_MEM(data,elen+1);
        memcpy(data,str,slen);
        if(NULL!=Z85_encode(encoded,data,elen)){
        encoded[esize]='0';
        OUT_CTTY("encoded:\r\n%s\r\n",encoded);
        }else{
        OUT_CTTY("Z85_encode err NULL\r\n");
        }

        size_t dsize=4*(strlen(encoded)+1)/5;
        byte decoded[dsize+1];
        OUT_CTTY("dsize[%zu]\r\n",dsize);
        ZERO_MEM(decoded,dsize);
        if(NULL!=Z85_decode(decoded,dsize,encoded)){
        decoded[dsize]='\0';
        OUT_CTTY("decoded:\"%s\"\r\n",(char *)decoded);
        }else{
        OUT_CTTY("Z85_decode err NULL\r\n");
        }
         return 0;
    }
#endif
#ifdef PROTO_MBUF
    int proto_test_mbuf()
    {
        uint32_t blank_ms=0;
        float period_sec=0.5;
        uint16_t n_samples=5;
        li_handler_fn handler=licor_handler_z;
        licor_mbuf_handle_t mbuf_id = LI_MBUF_CYCLE;
        uint32_t to_msec=90000;
        return_code_t retval=ERR_INVALID;

        if(argc>0)sscanf(argv[0],"%u",&n_samples);

        licor_mbuf_reset(LI_MBUF_CYCLE);
        licor_mbuf_set_rtid(LI_MBUF_CYCLE,RTID_ZRECv0);
        licor_mbuf_set_rmid(LI_MBUF_CYCLE,RMID_SAMPLE);
        to_msec=(unsigned long)(blank_ms+1200ul*period_sec*n_samples);

        int i=0;
        int nmeas = licor_mbuf_depth(LI_MBUF_CYCLE)-3;
        for(i=0;i<nmeas;i++)
            if((retval=licor_stream_x(blank_ms, period_sec, n_samples, handler, (void *)&mbuf_id, to_msec))==ERR_SUCCESS){
             licor_mbuf_set_mmid(LI_MBUF_CYCLE,i+1);
             licor_mbuf_next(LI_MBUF_CYCLE);
            }else{
                OUT_CTTY("ERR - licor_stream_x [%d]\r\n",retval);
            }
        licor_zrecord_show(licor_mbuf_record(mbuf_id), true, 5);
        return 0;
    }
#endif
#ifdef PROTO_MKBLOG
    int proto_test_mkblog()
    {
#define SAMPLE_PERIOD_SEC 600
#define SAMPLES_PER_PERIOD 2
#define SAMPLES_PER_HOUR (SAMPLES_PER_PERIOD*3600UL/SAMPLE_PERIOD_SEC)
#define SAMPLES_PER_DAY (SAMPLES_PER_HOUR*24)
#define LOG_DAYS 30
#define MEAS_COUNT 6

       uint32_t record_count=LOG_DAYS*SAMPLES_PER_DAY;
       Time tm_now;
       time_t tt_now;
       char DFL_FPATH[32]="DAT9999.XXX";
       char *fpath=DFL_FPATH;

#if defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
    FILE_T file_s, *fp=&file_s;
#else
    FILE_T *fp;
#endif

       int i=0;
       while(i<argc){
           OUT_CTTY("argc[%d] argv[%d] [%s]\r\n",argc,i,argv[i]);
           if(sscanf(argv[i],"C%"FMT_U32"",&record_count)==1){
                OUT_CTTY("record_count[%"FMT_U32"]\r\n",record_count);
           }else if(strstr(argv[i],"F:")!=NULL){
               fpath=argv[i]+2;
                OUT_CTTY("fpath[%s]\r\n",fpath);
           }
           i++;
       }

        if(iow_fopen(&fp, fpath, IOW_WRITE|IOW_CREATE_ALWAYS)==0){
           int i=0;

           UINT zrec_sz = LI_ZREC_SIZEN(MEAS_COUNT);
           byte zrec_mem[zrec_sz];
           licor_zrecord_t *zrec=(licor_zrecord_t *)zrec_mem;
           mdata_Z_t *mdata=LI_ZREC_PMDATA(zrec);

           // init time stamp with current time
           rtcRead(&tm_now);
           tt_now = mktime(&tm_now);
           uint64_t tems=1000LLU*tt_now;

           OUT_CTTY("writing %lu records...(q to quit)\r\n",record_count);
           for(i=0;i<record_count;i++){
               ZERO_MEM(zrec,zrec_sz);

               // BUG FIX: multiply by i overflows struct tm year
//               tems+=(uint64_t)(1000LLU*i*SAMPLE_PERIOD_SEC/SAMPLES_PER_PERIOD);
               tems+=(uint64_t)(1000LLU*SAMPLE_PERIOD_SEC/SAMPLES_PER_PERIOD);
               zrec->hdr.timestamp=tems;
               zrec->hdr.meas_id=i%RMID_COUNT;
               zrec->hdr.type_id=RTID_ZRECv0;
               zrec->hdr.seq_n=i;
               zrec->hdr.sync=LI_ZREC_SYNC;
               zrec->hdr.len=MEAS_COUNT*LI_ZREC_MDATA_LEN;
               zrec->hdr.meas_count=MEAS_COUNT;
               int j=0;
               size_t btw=0;
               size_t bw=0;
               for(j=0;j<MEAS_COUNT;j++){
                   btw=LI_ZREC_MDATA_LEN;
                   bw=0;
                   memset(&mdata[j],('A'+j),LI_ZREC_MDATA_LEN);
                   mdata[j].meas_id=i%MMID_COUNT;
                   mdata[j].period=0.5;
                   mdata[j].stat_n=5+j%10;
                   mdata[j].timestamps[0]=tems-20000LLU;
                   mdata[j].timestamps[1]=tems;
               }
               btw=zrec_sz;
               bw=0;
               if( (bw=iow_fwrite(fp,zrec,btw))==btw ){
                   if( i!=0 && (i%100) == 0){
                       OUT_CTTY("\b\b\b\b\b\b%6d",i);
                   }
               }else{
                   OUT_CTTY("\r\nwrite error fp[%"FMT_U32"] btw[%u] bw[%u] fr[%d]\r\n",iow_ftell(fp),btw,bw,iow_errno);
                   break;
               }
               byte ctrl=serGetChar(g_ctty);
               if(ctrl=='q' || ctrl=='Q')break;
           }
           iow_fclose(fp);
           OUT_CTTY("\r\ndone - wrote %d records\r\n",i);
       }
         return 0;
    }
#endif
#ifdef PROTO_PROGVARS
    int proto_test_progvars()
    {
         OUT_CTTY("before\r\n");
    //     OUT_CTTY("sys S/N[%04X] @%p\r\n",SerialNumber,pSN);
         OUT_CTTY("sys @%p S/N[%04X] foo[%04x] bar[%04x]\r\n",&sysinfo,sysinfo.sn,sysinfo.foo,sysinfo.bar);
         OUT_CTTY("after...\r\n");
         return 0;
    }
#endif
#ifdef PROTO_SEQ_API
int proto_test_sequence()
{
    int retval=0;
    const sequence_t *pseq=NULL;

//    OUT_CTTY("SEQ TABLE\r\n");
//    while(NULL!=pseq->name){
//        show_sequence(pseq,3);
//        OUT_CTTY("   ---\r\n");
//        pseq++;
//    }
    SYS_SET_MDFLAGS(MOD_ACT);
    seq_run(seq_ilookup(SEQ_ACID),NULL,NULL);

    pseq=seq_slookup("REST");
    seq_run(pseq,NULL,NULL);

    seq_param_t alt_logic={{123,456,-1},-1};

    OUT_CTTY(">>>alt_logic\r\n");
    show_logic(&alt_logic,5);

    seq_run(pseq,NULL,&alt_logic);

    MX_PULSE_100PCT(seq_io_ibits(IO_SAMPLE_VSET),MX_VALVE_TON_MSEC);
    MX_PULSE_100PCT(seq_io_sbits("+IOSAMPLE"),MX_VALVE_TON_MSEC);
    MX_PULSE_100PCT(seq_io_sbits("-IOSAMPLE"),MX_VALVE_TON_MSEC);

    const seq_actuation_t *action = seq_action_ilookup(IO_SAMPLE_PUMP);
    alt_logic.cycles=8;
    alt_logic.timing[LOGIC_P0]=MX_SPUMP_ON_MSEC;
    alt_logic.timing[LOGIC_P1]=MX_SPUMP_OFF_MSEC;
    alt_logic.timing[LOGIC_DELAY]=0;

    seq_action_run(action,&alt_logic);

    SYS_CLR_MDFLAGS(MOD_ACT);
   return retval;
}
#endif //PROTO_SEQ_API

#ifdef PROTO_SUMMARIZER
int proto_test_summarizer()
{
    int retval=-1;

    // !!! what can be done w/o temp?
    // can we use analog temp? estimate/default?

    // open orecord file (cycle file buf?)
    // find cycle start (-1:last) [no need if buffer]
    // process zcal -> parameters...
    // process scal -> parameters...
    // process air -> parameters...
    // process zero -> parameters...
    // process sample -> parameters...
    // run f(params)

    return retval;
}
#endif //PROTO_SUMMARIZER
#ifdef PROTO_SHT3X
#ifdef WITH_SHT3X
int proto_test_sht3x()
{
    int retval=-1;

    i2cp_hnd_t i2c_bus = SHT3X_I2C_HND;
    sht3x_hnd_t sht3x = SHT3X_PRI;
    byte bdat[]={'X','B','C','Y','E','F'};
    uint32_t bw=0;

    i2cp_initialize(i2c_bus);
    i2cp_set_debug(i2c_bus, true);

    BLINK_SET(1);
    i2cp_send_bytes(i2c_bus, bdat[0],&bdat[1],2,&bw);
    DELAY_MS(100UL);
    BLINK_SET(0);
    bw=0;
    i2cp_send_bytes(i2c_bus, bdat[3],&bdat[4],2,&bw);
    DELAY_MS(200UL);

    uint16_t cmd=CMD_SS_CSRH;//0x2c06
    i2c_addr_t addr=CMD_ADDR_A;//0x44 ( -> 0x88W/0x89R))
    uint32_t bwr=0;
    i2cp_send_bytes(i2c_bus, addr,(byte *)&cmd,2,&bwr);
    DELAY_MS(300UL);

    sht3x_ss_read(sht3x,10);
    DELAY_MS(400UL);

    sht3x_art_en(sht3x);
    DELAY_MS(2UL);
    sht3x_break(sht3x);
    DELAY_MS(2UL);
    sht3x_heater(sht3x,true);
    DELAY_MS(2UL);
    sht3x_heater(sht3x,false);
    DELAY_MS(2UL);
    byte sreg[3]={0};
    sht3x_sreg(sht3x,sreg,3);
    DELAY_MS(2UL);
    sht3x_clr_sreg(sht3x);
    DELAY_MS(500UL);

    return retval;
}
#endif //WITH_SHT3X
#endif //PROTO_SHT3X
return_code_t proto_test(int argc, char **argv)
{
    OUT_CTTY("\r\n>>> PROTO test\r\n");
    serTxWait(g_ctty);
#ifdef PROTO_SCHEDULE
    proto_test_schedule();
#endif
#ifdef PROTO_CRON
    proto_test_cron();
#endif
#ifdef PROTO_TASK
    proto_test_task();
#endif
#ifdef PROTO_SCRIPT
    proto_test_script();
#endif
#ifdef PROTO_PALLOC
    proto_test_palloc();
#endif
#ifdef PROTO_CONFIG
    proto_test_config();
#endif
#ifdef PROTO_SIZES
    proto_test_sizes();
#endif
#ifdef PROTO_NMEAS
    proto_test_nmeas();
#endif
#ifdef PROTO_QUEUE
    proto_test_queue();
#endif
#ifdef PROTO_UI_TOKENIZER
    proto_test_ui_tokenizer();
#endif
#ifdef PROTO_BUG_STRUCT_ALIGN
    proto_test_bug_struct_align();
#endif
#ifdef PROTO_FLAG_MNEM
    proto_test_flag_mnem();
#endif
#ifdef PROTO_TRAP_TEST
    proto_test_trap_test();
#endif
#ifdef PROTO_HEXSHOW_TEST
    proto_test_hexshow_test();
#endif
#ifdef PROTO_LESS_TEST
    proto_test_less_test();
#endif
#ifdef PROTO_SEEK_TEST
    proto_test_seek_test();
#endif
#ifdef PROTO_MAXSEG
    proto_test_maxseg();
#endif
#ifdef PROTO_LISTAT
    proto_test_listat();
#endif
#ifdef PROTO_DATA_SIZES
    proto_test_data_sizes();
#endif
#ifdef PROTO_Z85
    proto_test_z85();
#endif
#ifdef PROTO_MBUF
    proto_test_mbuf();
#endif
#ifdef PROTO_MKBLOG
    proto_test_mkblog();
#endif
#ifdef PROTO_PROGVARS
    proto_test_progvars();
#endif
#ifdef PROTO_SEQ_API
   proto_test_sequence();
#endif
#ifdef PROTO_SUMMARIZER
   proto_test_summarizer();
#endif
#ifdef PROTO_SHT3X
#ifdef WITH_SHT3X
   proto_test_sht3x();
#endif
#endif
#ifdef PROTO_SEQ_USR
   // ~4% data memory
#define SEQ_USR_ACT_N 32
#define SEQ_USR_LOG_N 32
#define SEQ_USR_STE_N 64
#define SEQ_USR_SEQ_N 16
#define SEQ_USR_NAM_N 10

   static seq_param_t seq_log_table[SEQ_USR_LOG_N];
   static seq_actuation_t seq_act_table[SEQ_USR_ACT_N];
   static sequence_step_t seq_def_table[SEQ_USR_STE_N];
   static sequence_t seq_seq_table[SEQ_USR_SEQ_N];
   static char seq_nam_table[SEQ_USR_SEQ_N][SEQ_USR_NAM_N];
//   seq "name",steps,{io,action,p0,p1,delay,cycles}
   OUT_CTTY("  seq_param_t seq_log_table[%2d] %8zd\r\n",SEQ_USR_LOG_N,sizeof(seq_log_table));
   OUT_CTTY(" seq_actuation_t seq_act_table[%2d] %8zd\r\n",SEQ_USR_ACT_N,sizeof(seq_act_table));
   OUT_CTTY("   sequence_step_t seq_def_table[%2d] %8zd\r\n",SEQ_USR_STE_N,sizeof(seq_def_table));
   OUT_CTTY("        sequence_t seq_seq_table[%2d] %8zd\r\n",SEQ_USR_SEQ_N,sizeof(seq_seq_table));
   OUT_CTTY("              char seq_nam_table[%2d][%2d] %4zd\r\n",SEQ_USR_SEQ_N,SEQ_USR_NAM_N,sizeof(seq_nam_table));
   size_t sum=(sizeof(seq_log_table)+sizeof(seq_act_table)+sizeof(seq_def_table)+sizeof(seq_seq_table)+sizeof(seq_nam_table));
   OUT_CTTY("Total: %zd bytes (%.1f %%)\r\n",sum,(100.0*sum/32768.0));
#endif

#ifdef PROTO_X
   OUT_CTTY("PROTO_X - OK\r\n");
#endif

   OUT_CTTY("<<< PROTO test\r\n");

    return ERR_SUCCESS;
}// proto_test()

#else // NOT WITH_PROTO
return_code_t proto_test(int argc, char **argv)
{
        return 0;
}
#endif // WITH_PROTO
