--- a/targets/Win32/plc_Win32_main.c Thu Jun 07 13:45:35 2018 +0300
+++ b/targets/Win32/plc_Win32_main.c Thu Jun 07 14:04:45 2018 +0300
@@ -54,11 +54,19 @@
+int ForceSaveRetainReq(void) { /* Variable used to stop plcloop thread */
- while(WaitForSingleObject(PLC_timer, INFINITE) == WAIT_OBJECT_0)
+ if (WaitForSingleObject(PLC_timer, INFINITE) != WAIT_OBJECT_0) @@ -241,43 +249,6 @@
WaitForSingleObject(python_sem, INFINITE);
-void CleanupRetain(void)
-int CheckRetainBuffer(void)
-void ValidateRetainBuffer(void)
-void InValidateRetainBuffer(void)
-void Retain(unsigned int offset, unsigned int count, void * p)
- for(position=0; position<count; position++ ){
- printf("%d : 0x%2.2x\n", offset+position, ((char*)p)[position]);
-void Remind(unsigned int offset, unsigned int count, void *p)
static void __attribute__((constructor))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Win32/plc_Win32_main_retain.c Thu Jun 07 14:04:45 2018 +0300
@@ -0,0 +1,300 @@
+const char rb_file[] = "retain_buffer_file"; +const char rb_file_bckp[] = "retain_buffer_file.bak"; +/* Retain header struct. */ + uint32_t header_offset; +/* Init retain info structure. */ +struct retain_info_t retain_info; +/* CRC lookup table and initial state. */ +uint32_t crc32_table[256]; +/* Generate CRC32 lookup table. */ +void GenerateCRC32Table(void) + /* Use CRC-32-IEEE 802.3 polynomial 0x04C11DB7 (bit reflected). */ + uint32_t poly = 0xEDB88320; + for (i = 0; i <= 0xFF; i++) + for (j = 0 ; j < 8 ; j++) + c = (c & 1) ? (c >> 1 ) ^ poly : (c >> 1); +/* Calculate CRC32 for len bytes from pointer buf with init starting value. */ +uint32_t GenerateCRC32Sum(const void* buf, unsigned int len, uint32_t init) + unsigned char* current = (unsigned char*) buf; + crc = crc32_table[(crc ^ *current++) & 0xFF] ^ (crc >> 8); +/* Calc CRC32 for retain file byte by byte. */ +int CheckFileCRC(FILE* file_buffer) + /* Set the magic constant for one-pass CRC calc according to ZIP CRC32. */ + const uint32_t magic_number = 0x2144df1c; + /* CRC initial state. */ + uint32_t calc_crc32 = 0; + while(!feof(file_buffer)){ + if (fread(&data_block, sizeof(data_block), 1, file_buffer)) + calc_crc32 = GenerateCRC32Sum(&data_block, sizeof(char), calc_crc32); + /* Compare crc result with a magic number. */ + return (calc_crc32 == magic_number) ? 1 : 0; +/* Compare current hash with hash from file byte by byte. */ + int offset = sizeof(retain_info.retain_size); + fseek(retain_buffer, offset , SEEK_SET); + fread(&size, sizeof(size), 1, retain_buffer); + if (size != retain_info.hash_size) + for(k = 0; k < retain_info.hash_size; k++){ + fread(&file_digit, sizeof(char), 1, retain_buffer); + if (file_digit != *(retain_info.hash+k)) + /* Generate CRC32 lookup table. */ + /* Get retain size in bytes */ + retain_info.retain_size = GetRetainSize(); + /* Hash stored in retain file as array of char in hex digits + (that's why we divide strlen in two). */ + retain_info.hash_size = PLC_ID ? strlen(PLC_ID)/2 : 0; + //retain_info.hash_size = 0; + retain_info.hash = malloc(retain_info.hash_size); + /* Transform hash string into byte sequence. */ + for (i = 0; i < retain_info.hash_size; i++) { + sscanf((PLC_ID + i*2), "%02X", &byte); + retain_info.hash[i] = byte; + /* Calc header offset. */ + retain_info.header_offset = sizeof(retain_info.retain_size) + \ + sizeof(retain_info.hash_size) + \ + /* Set header CRC initial state. */ + retain_info.header_crc = 0; + /* Calc crc for header. */ + retain_info.header_crc = GenerateCRC32Sum( + &retain_info.retain_size, + sizeof(retain_info.retain_size), + retain_info.header_crc); + retain_info.header_crc = GenerateCRC32Sum( + &retain_info.hash_size, + sizeof(retain_info.hash_size), + retain_info.header_crc); + retain_info.header_crc = GenerateCRC32Sum( + retain_info.header_crc); +void CleanupRetain(void) + /* Free hash memory. */ + free(retain_info.hash); +int CheckRetainFile(const char * file) + retain_buffer = fopen(file, "rb"); + /* Check CRC32 and hash. */ + if (CheckFileCRC(retain_buffer)) +int CheckRetainBuffer(void) + if (!retain_info.retain_size) + /* Check latest retain file. */ + if (CheckRetainFile(rb_file)) + /* Check if we have backup. */ + if (CheckRetainFile(rb_file_bckp)) + /* We don't have any valid retain buffer - nothing to remind. */ +#ifndef FILE_RETAIN_SAVE_PERIOD_S +#define FILE_RETAIN_SAVE_PERIOD_S 1.0 +static double CalcDiffSeconds(IEC_TIME* t1, IEC_TIME *t2) + t1->tv_sec - t2->tv_sec, + t1->tv_nsec - t2->tv_nsec + if ((dt.tv_nsec < -1000000000) || ((dt.tv_sec > 0) && (dt.tv_nsec < 0))){ + dt.tv_nsec += 1000000000; + if ((dt.tv_nsec > +1000000000) || ((dt.tv_sec < 0) && (dt.tv_nsec > 0))){ + dt.tv_nsec -= 1000000000; + return dt.tv_sec + 1e-9*dt.tv_nsec; +int RetainSaveNeeded(void) + static IEC_TIME last_save; + if (!retain_info.retain_size) + /* periodic retain flush to avoid high I/O load */ + diff_s = CalcDiffSeconds(&now, &last_save); + if ((diff_s > FILE_RETAIN_SAVE_PERIOD_S) || ForceSaveRetainReq()) { +void ValidateRetainBuffer(void) + /* Add retain data CRC to the end of buffer file. */ + fseek(retain_buffer, 0, SEEK_END); + fwrite(&retain_crc, sizeof(uint32_t), 1, retain_buffer); + /* Sync file buffer and close file. */ + fsync(fileno(retain_buffer)); +void InValidateRetainBuffer(void) + if (!RetainSaveNeeded()) + /* Rename old retain file into *.bak if it exists. */ + rename(rb_file, rb_file_bckp); + /* Set file CRC initial value. */ + retain_crc = retain_info.header_crc; + /* Create new retain file. */ + retain_buffer = fopen(rb_file, "wb+"); + fprintf(stderr, "Failed to create retain file : %s\n", rb_file); + /* Write header to the new file. */ + fwrite(&retain_info.retain_size, + sizeof(retain_info.retain_size), 1, retain_buffer); + fwrite(&retain_info.hash_size, + sizeof(retain_info.hash_size), 1, retain_buffer); + fwrite(retain_info.hash , + sizeof(char), retain_info.hash_size, retain_buffer); +void Retain(unsigned int offset, unsigned int count, void *p) + /* Generate CRC 32 for each data block. */ + retain_crc = GenerateCRC32Sum(p, count, retain_crc); + /* Save current var in file. */ + fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); + fwrite(p, count, 1, retain_buffer); +void Remind(unsigned int offset, unsigned int count, void *p) + /* Remind variable from file. */ + fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); + fread((void *)p, count, 1, retain_buffer);