andrej@2178: /* andrej@2178: This file is part of Beremiz, a Integrated Development Environment for andrej@2178: programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@2178: andrej@2178: See COPYING.runtime andrej@2178: andrej@2178: Copyright (C) 2018: Sergey Surkov andrej@2178: Copyright (C) 2018: Andrey Skvortsov andrej@2178: andrej@2178: */ andrej@2178: andrej@2173: #ifndef HAVE_RETAIN andrej@2173: #include andrej@2173: #include andrej@2173: #include andrej@2173: #include "iec_types.h" andrej@2173: andrej@2227: int GetRetainSize(void); andrej@2173: andrej@2173: /* Retain buffer. */ andrej@2173: FILE *retain_buffer; andrej@2173: const char rb_file[] = "retain_buffer_file"; andrej@2173: const char rb_file_bckp[] = "retain_buffer_file.bak"; andrej@2173: andrej@2173: andrej@2173: /* Retain header struct. */ andrej@2173: struct retain_info_t { andrej@2173: uint32_t retain_size; andrej@2173: uint32_t hash_size; andrej@2173: uint8_t* hash; andrej@2173: uint32_t header_offset; andrej@2173: uint32_t header_crc; andrej@2173: }; andrej@2173: andrej@2173: /* Init retain info structure. */ andrej@2173: struct retain_info_t retain_info; andrej@2173: andrej@2173: /* CRC lookup table and initial state. */ andrej@2173: uint32_t crc32_table[256]; andrej@2173: uint32_t retain_crc; andrej@2173: andrej@2173: andrej@2173: /* Generate CRC32 lookup table. */ andrej@2173: void GenerateCRC32Table(void) andrej@2173: { andrej@2173: unsigned int i, j; andrej@2173: /* Use CRC-32-IEEE 802.3 polynomial 0x04C11DB7 (bit reflected). */ andrej@2173: uint32_t poly = 0xEDB88320; andrej@2173: andrej@2173: for (i = 0; i <= 0xFF; i++) andrej@2173: { andrej@2173: uint32_t c = i; andrej@2173: for (j = 0 ; j < 8 ; j++) andrej@2173: c = (c & 1) ? (c >> 1 ) ^ poly : (c >> 1); andrej@2173: crc32_table[i] = c; andrej@2173: } andrej@2173: } andrej@2173: andrej@2173: andrej@2173: /* Calculate CRC32 for len bytes from pointer buf with init starting value. */ andrej@2173: uint32_t GenerateCRC32Sum(const void* buf, unsigned int len, uint32_t init) andrej@2173: { andrej@2173: uint32_t crc = ~init; andrej@2173: unsigned char* current = (unsigned char*) buf; andrej@2173: while (len--) andrej@2173: crc = crc32_table[(crc ^ *current++) & 0xFF] ^ (crc >> 8); andrej@2173: return ~crc; andrej@2173: } andrej@2173: andrej@2173: /* Calc CRC32 for retain file byte by byte. */ andrej@2173: int CheckFileCRC(FILE* file_buffer) andrej@2173: { andrej@2173: /* Set the magic constant for one-pass CRC calc according to ZIP CRC32. */ andrej@2173: const uint32_t magic_number = 0x2144df1c; andrej@2173: andrej@2173: /* CRC initial state. */ andrej@2173: uint32_t calc_crc32 = 0; andrej@2173: char data_block = 0; andrej@2173: andrej@2173: while(!feof(file_buffer)){ andrej@2173: if (fread(&data_block, sizeof(data_block), 1, file_buffer)) andrej@2173: calc_crc32 = GenerateCRC32Sum(&data_block, sizeof(char), calc_crc32); andrej@2173: } andrej@2173: andrej@2173: /* Compare crc result with a magic number. */ andrej@2173: return (calc_crc32 == magic_number) ? 1 : 0; andrej@2173: } andrej@2173: andrej@2173: /* Compare current hash with hash from file byte by byte. */ andrej@2173: int CheckFilehash(void) andrej@2173: { andrej@2173: int k; andrej@2173: int offset = sizeof(retain_info.retain_size); andrej@2173: andrej@2173: rewind(retain_buffer); andrej@2173: fseek(retain_buffer, offset , SEEK_SET); andrej@2173: andrej@2173: uint32_t size; andrej@2173: fread(&size, sizeof(size), 1, retain_buffer); andrej@2173: if (size != retain_info.hash_size) andrej@2173: return 0; andrej@2173: andrej@2173: for(k = 0; k < retain_info.hash_size; k++){ andrej@2173: uint8_t file_digit; andrej@2173: fread(&file_digit, sizeof(char), 1, retain_buffer); andrej@2173: if (file_digit != *(retain_info.hash+k)) andrej@2173: return 0; andrej@2173: } andrej@2173: andrej@2173: return 1; andrej@2173: } andrej@2173: andrej@2173: void InitRetain(void) andrej@2173: { andrej@2173: int i; andrej@2173: andrej@2173: /* Generate CRC32 lookup table. */ andrej@2173: GenerateCRC32Table(); andrej@2173: andrej@2173: /* Get retain size in bytes */ andrej@2173: retain_info.retain_size = GetRetainSize(); andrej@2173: andrej@2173: /* Hash stored in retain file as array of char in hex digits andrej@2173: (that's why we divide strlen in two). */ andrej@2173: retain_info.hash_size = PLC_ID ? strlen(PLC_ID)/2 : 0; andrej@2173: //retain_info.hash_size = 0; andrej@2173: retain_info.hash = malloc(retain_info.hash_size); andrej@2173: andrej@2173: /* Transform hash string into byte sequence. */ andrej@2173: for (i = 0; i < retain_info.hash_size; i++) { andrej@2173: int byte = 0; andrej@2173: sscanf((PLC_ID + i*2), "%02X", &byte); andrej@2173: retain_info.hash[i] = byte; andrej@2173: } andrej@2173: andrej@2173: /* Calc header offset. */ andrej@2173: retain_info.header_offset = sizeof(retain_info.retain_size) + \ andrej@2173: sizeof(retain_info.hash_size) + \ andrej@2173: retain_info.hash_size; andrej@2173: andrej@2173: /* Set header CRC initial state. */ andrej@2173: retain_info.header_crc = 0; andrej@2173: andrej@2173: /* Calc crc for header. */ andrej@2173: retain_info.header_crc = GenerateCRC32Sum( andrej@2173: &retain_info.retain_size, andrej@2173: sizeof(retain_info.retain_size), andrej@2173: retain_info.header_crc); andrej@2173: andrej@2173: retain_info.header_crc = GenerateCRC32Sum( andrej@2173: &retain_info.hash_size, andrej@2173: sizeof(retain_info.hash_size), andrej@2173: retain_info.header_crc); andrej@2173: andrej@2173: retain_info.header_crc = GenerateCRC32Sum( andrej@2173: retain_info.hash, andrej@2173: retain_info.hash_size, andrej@2173: retain_info.header_crc); andrej@2173: } andrej@2173: andrej@2173: void CleanupRetain(void) andrej@2173: { andrej@2173: /* Free hash memory. */ andrej@2173: free(retain_info.hash); andrej@2173: } andrej@2173: andrej@2173: int CheckRetainFile(const char * file) andrej@2173: { andrej@2173: retain_buffer = fopen(file, "rb"); andrej@2173: if (retain_buffer) { andrej@2173: /* Check CRC32 and hash. */ andrej@2173: if (CheckFileCRC(retain_buffer)) andrej@2173: if (CheckFilehash()) andrej@2173: return 1; andrej@2173: fclose(retain_buffer); andrej@2173: retain_buffer = NULL; andrej@2173: } andrej@2173: return 0; andrej@2173: } andrej@2173: andrej@2173: int CheckRetainBuffer(void) andrej@2173: { andrej@2173: retain_buffer = NULL; andrej@2173: if (!retain_info.retain_size) andrej@2173: return 1; andrej@2173: andrej@2173: /* Check latest retain file. */ andrej@2173: if (CheckRetainFile(rb_file)) andrej@2173: return 1; andrej@2173: andrej@2173: /* Check if we have backup. */ andrej@2173: if (CheckRetainFile(rb_file_bckp)) andrej@2173: return 1; andrej@2173: andrej@2173: /* We don't have any valid retain buffer - nothing to remind. */ andrej@2173: return 0; andrej@2173: } andrej@2173: andrej@2173: #ifndef FILE_RETAIN_SAVE_PERIOD_S andrej@2173: #define FILE_RETAIN_SAVE_PERIOD_S 1.0 andrej@2173: #endif andrej@2173: andrej@2173: static double CalcDiffSeconds(IEC_TIME* t1, IEC_TIME *t2) andrej@2173: { andrej@2173: IEC_TIME dt ={ andrej@2173: t1->tv_sec - t2->tv_sec, andrej@2173: t1->tv_nsec - t2->tv_nsec andrej@2173: }; andrej@2173: andrej@2173: if ((dt.tv_nsec < -1000000000) || ((dt.tv_sec > 0) && (dt.tv_nsec < 0))){ andrej@2173: dt.tv_sec--; andrej@2173: dt.tv_nsec += 1000000000; andrej@2173: } andrej@2173: if ((dt.tv_nsec > +1000000000) || ((dt.tv_sec < 0) && (dt.tv_nsec > 0))){ andrej@2173: dt.tv_sec++; andrej@2173: dt.tv_nsec -= 1000000000; andrej@2173: } andrej@2173: return dt.tv_sec + 1e-9*dt.tv_nsec; andrej@2173: } andrej@2173: andrej@2173: andrej@2173: int RetainSaveNeeded(void) andrej@2173: { andrej@2173: int ret = 0; andrej@2173: static IEC_TIME last_save; andrej@2173: IEC_TIME now; andrej@2173: double diff_s; andrej@2173: andrej@2173: /* no retain */ andrej@2173: if (!retain_info.retain_size) andrej@2173: return 0; andrej@2173: andrej@2173: /* periodic retain flush to avoid high I/O load */ andrej@2173: PLC_GetTime(&now); andrej@2173: andrej@2173: diff_s = CalcDiffSeconds(&now, &last_save); andrej@2173: andrej@2173: if ((diff_s > FILE_RETAIN_SAVE_PERIOD_S) || ForceSaveRetainReq()) { andrej@2173: ret = 1; andrej@2173: last_save = now; andrej@2173: } andrej@2173: return ret; andrej@2173: } andrej@2173: andrej@2173: void ValidateRetainBuffer(void) andrej@2173: { andrej@2173: if (!retain_buffer) andrej@2173: return; andrej@2173: andrej@2173: /* Add retain data CRC to the end of buffer file. */ andrej@2173: fseek(retain_buffer, 0, SEEK_END); andrej@2173: fwrite(&retain_crc, sizeof(uint32_t), 1, retain_buffer); andrej@2173: andrej@2173: /* Sync file buffer and close file. */ andrej@2173: #ifdef __WIN32 andrej@2173: fflush(retain_buffer); andrej@2173: #else andrej@2173: fsync(fileno(retain_buffer)); andrej@2173: #endif andrej@2173: andrej@2173: fclose(retain_buffer); andrej@2173: retain_buffer = NULL; andrej@2173: } andrej@2173: andrej@2173: void InValidateRetainBuffer(void) andrej@2173: { andrej@2173: if (!RetainSaveNeeded()) andrej@2173: return; andrej@2173: andrej@2173: /* Rename old retain file into *.bak if it exists. */ andrej@2173: rename(rb_file, rb_file_bckp); andrej@2173: andrej@2173: /* Set file CRC initial value. */ andrej@2173: retain_crc = retain_info.header_crc; andrej@2173: andrej@2173: /* Create new retain file. */ andrej@2173: retain_buffer = fopen(rb_file, "wb+"); andrej@2173: if (!retain_buffer) { andrej@2173: fprintf(stderr, "Failed to create retain file : %s\n", rb_file); andrej@2173: return; andrej@2173: } andrej@2173: andrej@2173: /* Write header to the new file. */ andrej@2173: fwrite(&retain_info.retain_size, andrej@2173: sizeof(retain_info.retain_size), 1, retain_buffer); andrej@2173: fwrite(&retain_info.hash_size, andrej@2173: sizeof(retain_info.hash_size), 1, retain_buffer); andrej@2173: fwrite(retain_info.hash , andrej@2173: sizeof(char), retain_info.hash_size, retain_buffer); andrej@2173: } andrej@2173: andrej@2173: void Retain(unsigned int offset, unsigned int count, void *p) andrej@2173: { andrej@2173: if (!retain_buffer) andrej@2173: return; andrej@2173: andrej@2173: /* Generate CRC 32 for each data block. */ andrej@2173: retain_crc = GenerateCRC32Sum(p, count, retain_crc); andrej@2173: andrej@2173: /* Save current var in file. */ andrej@2173: fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); andrej@2173: fwrite(p, count, 1, retain_buffer); andrej@2173: } andrej@2173: andrej@2173: void Remind(unsigned int offset, unsigned int count, void *p) andrej@2173: { andrej@2173: /* Remind variable from file. */ andrej@2173: fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); andrej@2173: fread((void *)p, count, 1, retain_buffer); andrej@2173: } andrej@2173: #endif // !HAVE_RETAIN