Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

SnConfigFrame.cpp

Committer:
uci1
Date:
2019-06-05
Revision:
125:ce4045184366
Parent:
124:4637a6546cad

File content as of revision 125:ce4045184366:

#include "SnConfigFrame.h"

#include "mbed.h"

//#define DEBUG

#include "SnBitUtils.h"
#include "SnHeaderFrame.h"
#include "Watchdog.h"
#include "SnSDUtils.h"

extern "C" void mbed_mac_address(char *);

bool              SnConfigFrame::fgApplySafetyNets     = true;
#if CHIPBOARD==ATWD4CH
const uint8_t     SnConfigFrame::kIOVers               = 11;
#elif CHIPBOARD==SST4CH
const uint8_t     SnConfigFrame::kIOVers               = 12;
#elif CHIPBOARD==SST4CH_1GHz
const uint8_t     SnConfigFrame::kIOVers               = 13;
#elif CHIPBOARD==SST4CH512
const uint8_t     SnConfigFrame::kIOVers               = 14;
#elif CHIPBOARD==SST4CH512_1GHz
const uint8_t     SnConfigFrame::kIOVers               = 15;
#elif CHIPBOARD==SST8CH
const uint8_t     SnConfigFrame::kIOVers               = 16;
#elif CHIPBOARD==SST8CH_1GHz
const uint8_t     SnConfigFrame::kIOVers               = 17;
#else
#error CHIPBOARD value not used in config frame i/o version
#endif

#if defined(LOAD_DEFAULT_CONFIG_FROM_SD)
const char* const SnConfigFrame::kDefConfFile          = "/sd/DEFCONF.DAT";
const char* const SnConfigFrame::kDefRemIpFilen        = "/sd/REMOTIP.TXT";
const char* const SnConfigFrame::kDefRemPortFilen      = "/sd/REMOTPRT.TXT";
const char* const SnConfigFrame::kDefMbedIPFilen       = "/sd/MBEDIP.TXT";
const char* const SnConfigFrame::kDefMbedMaskFilen     = "/sd/MBEDMASK.TXT";
const char* const SnConfigFrame::kDefMbedGateFilen     = "/sd/MBEDGATE.TXT";
#elif defined(USE_INTERFACE_CHIP)
const char* const SnConfigFrame::kDefConfFile          = "/local/DEFCONF.DAT";
const char* const SnConfigFrame::kDefRemIpFilen        = "/local/REMOTIP.TXT";
const char* const SnConfigFrame::kDefRemPortFilen      = "/local/REMOTPRT.TXT";
const char* const SnConfigFrame::kDefMbedIPFilen       = "/local/MBEDIP.TXT";
const char* const SnConfigFrame::kDefMbedMaskFilen     = "/local/MBEDMASK.TXT";
const char* const SnConfigFrame::kDefMbedGateFilen     = "/local/MBEDGATE.TXT";
#endif

const char* const SnConfigFrame::kDefIPflag            = "DEF";
const uint32_t    SnConfigFrame::kDefIPvalue           = 0;
const uint32_t    SnConfigFrame::kMinCommWinPrdLowPwr  = 14400;    // exclusive min low power comm win period (s)
const uint32_t    SnConfigFrame::kMaxCommWinPrdLowPwr  = 259200;   // exclusive max low power comm win period (s)
const uint32_t    SnConfigFrame::kMinCommWinDurLowPwr  = 300;      // exclusive min low power comm win duration (s)
const uint32_t    SnConfigFrame::kMaxCommWinDurLowPwr  = 3600;     // exclusive max low power comm win duration (s)
const uint8_t     SnConfigFrame::kConfLblLen;

uint64_t       SnConfigFrame::fgMacAdr              = 0;

void SnConfigFrame::SetMacAddress() {
    static const uint8_t b64 = sizeof(uint64_t);
    static char c[b64];
    // NOTE: the function below may not be the one provided by MBED!
    // see main.cpp
    mbed_mac_address(&(c[0]));
    // like a big endian union
    fgMacAdr = 0;
    const char* a = c+(b64-1);
    for (uint8_t i=0; i<b64; i++, a--) {
        fgMacAdr |= static_cast<uint64_t>(*a) << (i<<3);
    }
#ifdef DEBUG
    printf("MAC=%012llX\r\n", fgMacAdr>>16); // 64 -> 48 bits
#endif
}

void SnConfigFrame::SetHardDefaults() {
    sprintf(fLabel,"Default_rev124");
    fConfTime           = 1338854400u; // Tue, 05 Jun 2012 00:00:00 GMT
    fRun                = 0;
    fFirstSeq           = 0;
    fEvtsPerSeq         = 300;
#if CHIPBOARD==ATWD4CH
    fRunMode            = kRSListOneCW;
    fStreamHiLoPlas     = 0;
#else // not ATWD4CH
    fRunMode            = kDualThreshBit | kDiffTrigBit | kRSListOneCW;
#endif
    fWvLoseLSB          = 0;
    fWvLoseMSB          = 4;
    fWvBaseline         = 0;
    fDatPackType        = kSDcard | kIrid | kAfar | kUSB; // starting with io vers 11+, this is no longer changeable
    uint16_t* dc = &(fDAC[0][0]);
    for (uint16_t i=0; i<kTotDacs; i++, dc++) {
        *dc             = 3072u;
    }
#if CHIPBOARD==ATWD4CH
    fNumPlas            = 1;
    uint16_t* pl = &(fPLA[0]);
    for (uint8_t j=0; j<kNplas; j++, pl++) {
        *pl             = 0x7FFFu;
    }
#endif
    fNumCardsMajLog     = 2;
    fEnableThermTrig    = 0;
    fForceTrigPeriod    = 0;
    fHeartBeatPeriod    = 0;
    //fAmpsOn             = 0x0Fu; removed vers 11+
    fEvtThrtlPeriodMs   = 0;
#ifdef AFAR_ON_FIRST_COMM_HARDCONF
    fPowerMode          = kIridComWin | kAfarComWin;
#else
    fPowerMode          = kIridComWin; 
#endif
    fBatVoltToLowPwr    = 0;
    fBatVoltFromLowPwr  = 0;
    fVoltCheckPeriod    = 127u; 
    fCommWinPeriod      = 300u;
    fCommWinDuration    = 600u;
    fCommSendData       = static_cast<int16_t>(kUseBits);
    fCommWinPrdLowPwr   = 86100u;
    fCommWinDurLowPwr   = 300u;
    for (uint8_t i=0; i<kNumDatStreams; ++i) {
        fCommWinConnTOMins[i] = GetDefaultConnTOMinOf(GetDataStreamForIndex(i));
        fCommWinListTOMins[i] = GetDefaultListTOMinOf(GetDataStreamForIndex(i));
    }
    fWatchDogPeriod     = WDFAILSAFE;
#if CHIPBOARD==ATWD4CH
//    fTempCheckPeriod    = 0;
#else // not ATWD4CH
    fTempCheckPeriod    = -8;
#endif
    SetDefaultIPs();
    fIsLowPower         = false;
    memcpy(fNormLabel, fLabel, kConfLblLen);
    fNormPowerMode      = fPowerMode;
    
    ApplyConnectListenSafetyNets();
}

void SnConfigFrame::SetDefaultRemoteServ() {
    if (false==ReadDefaultRemoteServer()) {
        sprintf(fRemoteServer,"%s","128.195.204.151");
    }
}

void SnConfigFrame::SetDefaultRemotePort() {
    if (false==ReadDefaultRemotePort()) {
        fRemotePort         = 6655;
    }
}

void SnConfigFrame::SetDefaultMbedIP() {
    // see ReadDefaultIPFile
    //
    // these hardcoded defaults are only here
    // "in case". they make it difficult/impossible
    // to swap boards in the field without internet
    // access to change the mbed program
    const uint64_t ip = GetMacAddress();
    if (false==ReadDefaultMbedIP()) {
        switch (ip) {
            case 0x0002F7F0AEE00000: // stn 20, atwd mb 1
                sprintf(fMbedIP,"%s","157.132.94.30");
                break;
            case 0x0002F7F2244B0000: // stn 13, board 102
                sprintf(fMbedIP,"%s","157.132.94.31");
                break;
            case 0x0002F7F20A9C0000: // stn 14, board 104
                sprintf(fMbedIP,"%s","157.132.94.33");
                break;
            case 0x0002F7F21A8A0000: // board 111
                sprintf(fMbedIP,"%s","157.132.94.34");
                break;                
            case 0x0002F7F0C3B60000: // station 3, atwd mb 5
                sprintf(fMbedIP,"%s","157.132.94.35");
                break;
            case 0x0002F7F0C41C0000: // station 4
            case 0x0002F7F1F7A80000: // stn 15, board 110
                sprintf(fMbedIP,"%s","157.132.94.37");
                break;
            case 0x0002F7F0C61A0000: // station 10, atwd mb 8
                sprintf(fMbedIP,"%s","157.132.94.39");
                break;
            case 0x0002F7F0C4450000: // station 6
            case 0x0002F7F1E9ED0000: // HCR stn 40, board 108
                sprintf(fMbedIP,"%s","157.132.94.50");
                break;
            case 0x0002F7F1F7C60000: // stn41, board 101
                sprintf(fMbedIP,"%s","157.132.94.53");
                break;
            case 0x0002F7F224440000: // stn 19, board 105
                sprintf(fMbedIP,"%s","157.132.94.41");
                break;
            case 0x0002F7F175B70000: // station 11
            case 0x0002F7F202C10000: // stn 17, board 109
                sprintf(fMbedIP,"%s","157.132.94.43");
                break;
            case 0x0002F7F1F6340000: // stn 31, board 107
                sprintf(fMbedIP,"%s","157.132.94.47");
                break;
            case 0x0002F7F1F21A0000: // stn 32, board 112
                sprintf(fMbedIP,"%s","157.132.94.48");
                break;
            case 0x0002F7F1F2120000: // stn 30, board 113
                sprintf(fMbedIP,"%s","157.132.94.49");
                break;
            case 0x0002F7F2E24B0000: // stn 50, board 202
                sprintf(fMbedIP,"%s","157.132.94.26");
                break;
            case 0x0002F7F2E7B90000: // stn 51, board 203
                sprintf(fMbedIP,"%s","157.132.94.27");
                break;
            case 0x0002F7F2E1CE0000: // stn 52, board 204
                sprintf(fMbedIP,"%s","157.132.94.28");
                break;
            case 0x0002F7F2EC550000: // board 207
                sprintf(fMbedIP,"%s","157.132.94.29");
                break;
            case 0x0002F7F2EDFF0000: // board 208
                sprintf(fMbedIP,"%s","157.132.94.30");
                break;
            default: // what??
                sprintf(fMbedIP,"%s","157.132.94.46"); // usually for tent
                break;
        };
    }
}
// brian dornick 3107
void SnConfigFrame::SetDefaultMaskIP() {
    if (false==ReadDefaultMbedMask()) {
        sprintf(fMbedMask,"%s","255.255.255.0");
    }
}

void SnConfigFrame::SetDefaultGateIP() {
    if (false==ReadDefaultMbedGate()) {
        sprintf(fMbedGate,"%s","157.132.94.1");
    }
}

#ifdef USE_INTERFACE_CHIP
bool SnConfigFrame::ReadOneIPFrom(const char* ipfname,
                                  char* ipstr) {
    bool ok = false;
    FILE* ipf = fopen(ipfname, "r");
    if (ipf!=0) {
        uint8_t ipbytes[4] = {0,0,0,0};
        const int nr = fscanf(ipf,"%hhu.%hhu.%hhu.%hhu\n",
            &(ipbytes[0]), &(ipbytes[1]),
            &(ipbytes[2]), &(ipbytes[3]));
        if (4==nr) {
            const int nc = 
                sprintf(ipstr,"%hhu.%hhu.%hhu.%hhu",
                    ipbytes[0], ipbytes[1],
                    ipbytes[2], ipbytes[3]);
            ok = nc>0;
        }
        fclose(ipf);
    }
#ifdef DEBUG
    printf("ReadOneIPInto : ipstr=[%s], ok=%d\r\n",
        ipstr, (int)ok);
#endif
    return ok;
}
#endif

bool SnConfigFrame::ReadDefaultRemoteServer() {
#ifdef USE_INTERFACE_CHIP
    const bool ok = ReadOneIPFrom(kDefRemIpFilen, fRemoteServer);    
#ifdef DEBUG
    printf("remote = %s\r\n", fRemoteServer);
#endif
    return ok;
#else // do not USE_INTERFACE_CHIP
    return false;
#endif // USE_INTERFACE_CHIP
}

bool SnConfigFrame::ReadDefaultRemotePort() {
#ifdef USE_INTERFACE_CHIP
    bool ok = false;
    FILE* pf = fopen(kDefRemPortFilen, "r"); 
    if (pf!=0) {
        ok = (1==fscanf(pf, "%hu\n", &fRemotePort));
        fclose(pf);
    }
#ifdef DEBUG
    printf("port   = %hu\r\n", fRemotePort);
#endif
    return ok;
#else // do not USE_INTERFACE_CHIP
    return false;
#endif // USE_INTERFACE_CHIP
}

bool SnConfigFrame::ReadDefaultMbedIP() {
#ifdef USE_INTERFACE_CHIP
    const bool ok = ReadOneIPFrom(kDefMbedIPFilen, fMbedIP);    
#ifdef DEBUG
    printf("mbed   = %s\r\n", fMbedIP);
#endif
    return ok;
#else // do not USE_INTERFACE_CHIP
    return false;
#endif // USE_INTERFACE_CHIP
}

bool SnConfigFrame::ReadDefaultMbedMask() {
#ifdef USE_INTERFACE_CHIP
    const bool ok = ReadOneIPFrom(kDefMbedMaskFilen, fMbedMask);    
#ifdef DEBUG
    printf("mask   = %s\r\n", fMbedMask);
#endif
    return ok;
#else // do not USE_INTERFACE_CHIP
    return false;
#endif // USE_INTERFACE_CHIP
}

bool SnConfigFrame::ReadDefaultMbedGate() {
#ifdef USE_INTERFACE_CHIP
    const bool ok = ReadOneIPFrom(kDefMbedGateFilen, fMbedGate);
#ifdef DEBUG
    printf("gate   = %s\r\n", fMbedGate);
#endif
    return ok;
#else // do not USE_INTERFACE_CHIP
    return false;
#endif // USE_INTERFACE_CHIP
}


void SnConfigFrame::SetDefaultIPs() {
    GetMacAddress(); // just to make sure it gets read
    SetDefaultRemoteServ();
    SetDefaultRemotePort();
    SetDefaultMbedIP();
    SetDefaultMaskIP();
    SetDefaultGateIP();
}

void SnConfigFrame::ApplyConnectListenSafetyNets(const uint8_t dataStreamIdx) {
    const float maxto_f = (fCommWinDuration/(60.*static_cast<float>(kNcomms)));
    const uint8_t maxto = (maxto_f < kDefTimeoutMin) ? kDefTimeoutMin :
        (maxto_f > 255.0) ? kDefTimeoutSafe : static_cast<uint8_t>(maxto_f);
    if (fCommWinConnTOMins[dataStreamIdx] < kDefTimeoutMin) {
        fCommWinConnTOMins[dataStreamIdx] = kDefTimeoutMin;
    }
    if (fCommWinConnTOMins[dataStreamIdx] > maxto) {
        fCommWinConnTOMins[dataStreamIdx] = maxto;
    }
    if (fCommWinListTOMins[dataStreamIdx] < kDefTimeoutMin) {
        fCommWinListTOMins[dataStreamIdx] = kDefTimeoutMin;
    }
    if (fCommWinListTOMins[dataStreamIdx] > maxto) {
        fCommWinListTOMins[dataStreamIdx] = maxto;
    }
}

void SnConfigFrame::ApplyConnectListenSafetyNets() {
    for (uint8_t i=0; i<kNumDatStreams; ++i) {
        ApplyConnectListenSafetyNets(i);
    }
}

void SnConfigFrame::ApplySafetyNets() {
    if (fFirstSeq>kMaxFirstSeq) {
        fFirstSeq=kMaxFirstSeq;
    }
#if CHIPBOARD==ATWD4CH
    if (fNumPlas>kNplas) {
        fNumPlas=kNplas;
    }
#endif
    if (fNumCardsMajLog>kNchans) {
        fNumCardsMajLog=kNchans;
    }
    if (fNumCardsMajLog<1u) {
        fNumCardsMajLog=1u;
    }
    if ( (fForceTrigPeriod>0) &&
         (fForceTrigPeriod<kMinForcePer) ) {
        fForceTrigPeriod = kMinForcePer;
    }
    if (fEvtThrtlPeriodMs>kMaxThrottlePerMs) {
        fEvtThrtlPeriodMs=kMaxThrottlePerMs;
    }
    if ( (IsPoweredFor(kIridComWin)==false) &&
         (IsPoweredFor(kAfarComWin)==false) ) {
        EnablePowerFor(kIridComWin);
        EnablePowerFor(kAfarComWin);
    }
    if (fBatVoltToLowPwr>kMaxBatVoltLowPwr) {
        fBatVoltToLowPwr=kMaxBatVoltLowPwr;
    }
    if (fBatVoltFromLowPwr>kMaxBatVoltLowPwr) {
        fBatVoltFromLowPwr=kMaxBatVoltLowPwr;
    }
    if (fBatVoltFromLowPwr<fBatVoltToLowPwr) {
        fBatVoltFromLowPwr=fBatVoltToLowPwr;
    }
    if (fCommWinPeriod>kMaxCommWinPeriod) {
        fCommWinPeriod=kMaxCommWinPeriod;
    }
    if (fCommWinPeriod<kMinCommWinPeriod) {
        fCommWinPeriod=kMinCommWinPeriod;
    }
    if (fCommWinDuration<kMinCommWinDur) {
        fCommWinDuration=kMinCommWinDur;
    }
    if (fCommWinPrdLowPwr>kMaxCommWinPeriod) {
        fCommWinPrdLowPwr=kMaxCommWinPeriod;
    }
    if (fCommWinDurLowPwr<kMinCommWinDur) {
        fCommWinDurLowPwr=kMinCommWinDur;
    }
    ApplyConnectListenSafetyNets();
    if (fWatchDogPeriod>kMaxWatchDogPer) {
        fWatchDogPeriod=kMaxWatchDogPer;
    }
    if (fWatchDogPeriod<kMinWatchDogPer) {
        fWatchDogPeriod=kMinWatchDogPer;
    }
    if (fSnglFreqRatio<kMinSingleFreqSuppRatio) {
        fSnglFreqRatio=kDefSingleFreqSuppRatio;
    }
    if (fSnglFreqRatio>kMaxSingleFreqSuppRatio) {
        fSnglFreqRatio=kDefSingleFreqSuppRatio;
    }
#ifdef DEBUG
    printf("||||||||| safety: fSnglFreqRatio=%hhu\r\n", fSnglFreqRatio);
#endif
}

uint32_t SnConfigFrame::GetTimeoutTime(const uint32_t startTime,
                                       const uint32_t delta) const {
    //  --.-----lst------.--delta--.--
    //    .              .         .
    //   start        current    returnVal
    //
    //   lio=lst+delta bound by comm wind dur
    //   returns start + lio
    
    const uint32_t ct = time(0);
    uint32_t lst = ct-startTime;
    if ( (ct<startTime) || (ct==0) ||
         (lst>kSecsPerDay) ) {
        // possible clock problems
        lst = static_cast<uint32_t>(kDefTimeoutSafe)*60u;
    }
    const uint32_t lio = 
        ((lst+delta) < GetCommWinDuration()) ?
          lst+delta  : GetCommWinDuration();
    return lio+startTime;
}

void SnConfigFrame::ChangeToLowPower() {
    
    // save old label
    memcpy(fNormLabel, fLabel, kConfLblLen);
    
    // append label
    // this will allow the new config to be put in the DB
    int slen = strlen(fLabel);
    static const char* tag = "_LOWPOW";
    const int ml = strlen(tag)+1;
    if (slen > (kConfLblLen-ml) ) {
        memset(fLabel+kConfLblLen-ml, '\0', ml);
    }
    strncat(fLabel, tag, ml-1);
    
    // save power settings
    fNormPowerMode = fPowerMode;
    
    // change power settings
    DisablePowerFor(kAmpsDatTak);
    DisablePowerFor(kCardDatTak);
    DisablePowerFor(kIridDatTak);
    DisablePowerFor(kAfarDatTak);
    DisablePowerFor(kAmpsComWin);
    DisablePowerFor(kCardComWin);
    if ( IsSBDonlyLowPwrMode() ) {
        EnablePowerFor(kIridComWin);
        DisablePowerFor(kAfarComWin);
    } else if ( (IsPoweredFor(kIridComWin)==false) &&
                (IsPoweredFor(kAfarComWin)==false) ) {
        // TODO: turn on only iridum maybe?
        EnablePowerFor(kIridComWin);
        EnablePowerFor(kAfarComWin);
    } // else same as normal for Irid and Afar Com Win

    // set mode to low power
    fIsLowPower = true;

    if (fgApplySafetyNets) {
        ApplySafetyNets();
    }
}

void SnConfigFrame::ChangeToNormPower() {
    // put label back
    memcpy(fLabel, fNormLabel, kConfLblLen);
    // put power settings back
    fPowerMode = fNormPowerMode;
    // set mode to normal
    fIsLowPower = false;

    if (fgApplySafetyNets) {
        ApplySafetyNets();
    }
}

void SnConfigFrame::GetPackParsFor(const EDatPackBit d,
                                   uint8_t& loseLSB, uint8_t& loseMSB,
                                   uint16_t& wvBase) const {
    const bool pack = IsDatPackedFor(d);
    loseLSB = pack ? GetWvLoseLSB()  : 0u;
    loseMSB = pack ? GetWvLoseMSB()  : 0u;
    wvBase  = pack ? GetWvBaseline() : 0u;
}

void SnConfigFrame::GetHiLoPlas(const uint16_t pla,
                                uint16_t& hiPla,
                                uint16_t& loPla,
                                const bool r2l) {
   // split the PLA bitword into 2: one for the high threshold
   // and one for the low threshold. "lows" in the string will become
   // "highs" in the low threshold PLA.
   // 
   // example 1)
   // PLA string = HLHL....
   // hi thresh  = H.H.....
   // lo thresh  = .H.H....
   //
   // example 2)
   // PLA string = HBL.....
   // hi thresh  = HL......
   // lo thresh  = .LH.....
   //
   // (with . = A here, to make the example more readable)
   //
   // A = 11, B = 00
   // H = 01 or 10, alternating
   // L = 10 or 01, alternating
   // 01 at leftmost bits is H
   // for example:
   // 0x7FFF = 01 11 11 11 11 11 11 11
   // => HAAAAAAA for LEFT TO RIGHT
   // => AAAAAAAH for RIGHT TO LEFT
   // 0x56FF = 01 01 01 10 11 11 11 11
   // => HLHHAAAA for LEFT TO RIGHT
   // => AAAAHHLH for RIGHT TO LEFT
   //
   // so HHHHHHHH is
   // 01 10 01 10 01 10 01 10 always (r2l or l2r)
   //
   // r2l = whether to read bits right to left (true) or not (false)
   // Mahshid liked right to left
   // Liang liked left to right
   // so we allow for either
   
   const int8_t start = (r2l) ?             0 : BITS_IN_SHORT-2;
   const int8_t end   = (r2l) ? BITS_IN_SHORT : -2;
   const int8_t step  = (r2l) ?             2 : -2;
   
   uint8_t hi= (r2l) ? 0x2 : 0x1;
   uint8_t lo= (r2l) ? 0x1 : 0x2;
   
   // set all bits to 0
   hiPla = 0;
   loPla = 0;
   
   for (int8_t i=start; i!=end; i+=step, hi^=0x3, lo^=0x3) {
      const uint8_t b = (pla & (0x3<<i)) >> i;
      if (b==hi) {
         hiPla |=  hi << i;
         loPla |= 0x3 << i;
      } else if (b==lo) {
         hiPla |= 0x3 << i;
         loPla |=  hi << i;
      } else if (b==0x3) {
         // any
         hiPla |= 0x3 << i;
         loPla |= 0x3 << i;
      } else {
         // no check that b is something else.. should be impossible.
         // between
         hiPla |=  lo << i;
         loPla |=  lo << i;
      }
   }
   
}

bool SnConfigFrame::ReadFromFile(const char* cfile) {
    // intended only for reading default config file
    
    /*
    DIR* d;
    struct dirent* dent;
    printf("files in /local:\r\n");
    if ( (d = opendir( "/local" ))!=NULL ) {
        while ( (dent = readdir(d))!=NULL ) {
            printf("%s\r\n",dent->d_name);
        }
        closedir(d);
    }
    */
#ifdef DEBUG
    printf("trying to read config [%s]\r\n",cfile);
#endif
    bool ret = false;
    FILE* cf = fopen(cfile,"rb");
    if (cf!=0) {
#ifdef DEBUG
        printf("opened file\r\n");
#endif
        // check the header and file size to be
        // protect a bit against corruption
        uint8_t hc; uint32_t hl;
        SnHeaderFrame::ReadFrom(cf, hc, hl);
#ifdef DEBUG
        printf("hc=%hhu, hl=%u\r\n",hc,hl);
#endif
        if (hc==SnHeaderFrame::kConfigCode) {
            const int fpos = ftell(cf);
            // how many bytes?
            fseek(cf, 0, SEEK_END); // go to end
            const int fend = ftell(cf);
            fseek(cf, fpos, SEEK_SET); // go back

#ifdef DEBUG
            printf("fend-fpos=%d-%d=%d\r\n",fend,fpos,fend-fpos);
#endif            
            if (hl == fend-fpos) {
                ReadFrom(cf);
                ret = (ferror(cf)==0);
#ifdef DEBUG
                printf("ret = %d\r\n",(int)ret);
#endif
            }
        }
        fclose(cf);
    }
    return ret;
}

bool SnConfigFrame::WriteToFile(const char* cfile) const {
    // intended only for writing default config file
    
    bool ret = false;
    FILE* cf = fopen(cfile,"wb");
    if (cf!=0) {
        WriteTo(cf);
        ret = (ferror(cf)==0);
        fclose(cf);
    }
    return ret;
}

void SnConfigFrame::SetSDNeedToInitFlag() {
    // reset the SD card init cache, in case the SD ignore run mode changed
    SnSDUtils::SetDoNeedToInit();
}

void SnConfigFrame::GetIpStrFromVal(const uint32_t ip,
                                    char(& str)[SnConfigFrame::kIPLen]) {
    if (ip==kDefIPvalue) {
        snprintf(str, kIPLen, kDefIPflag);
    } else {
        // little endian, but same method is used in ipToVal so the
        // conversions will be consistent on this machine, but of course,
        // the remote machine better send the right integer...
        union {
          uint8_t  c[4];
          uint32_t u;
        } x;
        x.u = ip;
        snprintf(str, kIPLen, "%hhu.%hhu.%hhu.%hhu",
                x.c[3], x.c[2], x.c[1], x.c[0]);
    }
}

uint32_t SnConfigFrame::GetIpValFromStr(const char(& str)[SnConfigFrame::kIPLen]) {
    if (strncmp(str, kDefIPflag, kIPLen)==0) {
        return kDefIPvalue;
    } else {
        union {
          uint8_t  c[4];
          uint32_t u;
        } x;
        // 17 = space for terminating null
        // little endian, but same method is used in ipToVal so the
        // conversions will be consistent on this machine, but of course,
        // the remote machine better send the right integer...
        sscanf(str, "%hhu.%hhu.%hhu.%hhu",
              x.c+3, x.c+2, x.c+1, x.c);
        return x.u;
    }
}