# HG changeset patch # User Andrey Skvortsov # Date 1528369485 -10800 # Node ID 55611282b909050d8e13ab3081331072ac81e492 # Parent 976841968d745b8758d7ae26c6041d73f3312adb Use the same retain implementation for Win32 targets Currently plc_Win32_main_retain.c is exact copy of plc_Linux_main_retain.c, because it's not possible to use symbol links. It'd be nice in the future to have possibility to modify targets.code. diff -r 976841968d74 -r 55611282b909 targets/Win32/plc_Win32_main.c --- 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 PLC_shutdown; + +int ForceSaveRetainReq(void) { + return PLC_shutdown; +} + /* Variable used to stop plcloop thread */ void PlcLoop() { - while(WaitForSingleObject(PLC_timer, INFINITE) == WAIT_OBJECT_0) - { + PLC_shutdown = 0; + while(!PLC_shutdown) { + if (WaitForSingleObject(PLC_timer, INFINITE) != WAIT_OBJECT_0) + PLC_shutdown = 1; PLC_timer_notify(); } } @@ -241,43 +249,6 @@ WaitForSingleObject(python_sem, INFINITE); } -#ifndef HAVE_RETAIN -void InitRetain(void) -{ -} - -void CleanupRetain(void) -{ -} - -int CheckRetainBuffer(void) -{ - return 1; -} - -void ValidateRetainBuffer(void) -{ -} - -void InValidateRetainBuffer(void) -{ -} - -void Retain(unsigned int offset, unsigned int count, void * p) -{ - /* - unsigned int position; - for(position=0; position +#include +#include +#include "iec_types.h" + +int GetRetainSize(); + +/* Retain buffer. */ +FILE *retain_buffer; +const char rb_file[] = "retain_buffer_file"; +const char rb_file_bckp[] = "retain_buffer_file.bak"; + + +/* Retain header struct. */ +struct retain_info_t { + uint32_t retain_size; + uint32_t hash_size; + uint8_t* hash; + uint32_t header_offset; + uint32_t header_crc; +}; + +/* Init retain info structure. */ +struct retain_info_t retain_info; + +/* CRC lookup table and initial state. */ +uint32_t crc32_table[256]; +uint32_t retain_crc; + + +/* Generate CRC32 lookup table. */ +void GenerateCRC32Table(void) +{ + unsigned int i, j; + /* Use CRC-32-IEEE 802.3 polynomial 0x04C11DB7 (bit reflected). */ + uint32_t poly = 0xEDB88320; + + for (i = 0; i <= 0xFF; i++) + { + uint32_t c = i; + for (j = 0 ; j < 8 ; j++) + c = (c & 1) ? (c >> 1 ) ^ poly : (c >> 1); + crc32_table[i] = c; + } +} + + +/* Calculate CRC32 for len bytes from pointer buf with init starting value. */ +uint32_t GenerateCRC32Sum(const void* buf, unsigned int len, uint32_t init) +{ + uint32_t crc = ~init; + unsigned char* current = (unsigned char*) buf; + while (len--) + crc = crc32_table[(crc ^ *current++) & 0xFF] ^ (crc >> 8); + return ~crc; +} + +/* 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; + char data_block = 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 CheckFilehash(void) +{ + int k; + int offset = sizeof(retain_info.retain_size); + + rewind(retain_buffer); + fseek(retain_buffer, offset , SEEK_SET); + + uint32_t size; + fread(&size, sizeof(size), 1, retain_buffer); + if (size != retain_info.hash_size) + return 0; + + for(k = 0; k < retain_info.hash_size; k++){ + uint8_t file_digit; + fread(&file_digit, sizeof(char), 1, retain_buffer); + if (file_digit != *(retain_info.hash+k)) + return 0; + } + + return 1; +} + +void InitRetain(void) +{ + int i; + + /* Generate CRC32 lookup table. */ + GenerateCRC32Table(); + + /* 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++) { + int byte = 0; + 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) + \ + 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.hash, + retain_info.hash_size, + retain_info.header_crc); +} + +void CleanupRetain(void) +{ + /* Free hash memory. */ + free(retain_info.hash); +} + +int CheckRetainFile(const char * file) +{ + retain_buffer = fopen(file, "rb"); + if (retain_buffer) { + /* Check CRC32 and hash. */ + if (CheckFileCRC(retain_buffer)) + if (CheckFilehash()) + return 1; + fclose(retain_buffer); + retain_buffer = NULL; + } + return 0; +} + +int CheckRetainBuffer(void) +{ + retain_buffer = NULL; + if (!retain_info.retain_size) + return 1; + + /* Check latest retain file. */ + if (CheckRetainFile(rb_file)) + return 1; + + /* Check if we have backup. */ + if (CheckRetainFile(rb_file_bckp)) + return 1; + + /* We don't have any valid retain buffer - nothing to remind. */ + return 0; +} + +#ifndef FILE_RETAIN_SAVE_PERIOD_S +#define FILE_RETAIN_SAVE_PERIOD_S 1.0 +#endif + +static double CalcDiffSeconds(IEC_TIME* t1, IEC_TIME *t2) +{ + IEC_TIME dt ={ + 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_sec--; + dt.tv_nsec += 1000000000; + } + if ((dt.tv_nsec > +1000000000) || ((dt.tv_sec < 0) && (dt.tv_nsec > 0))){ + dt.tv_sec++; + dt.tv_nsec -= 1000000000; + } + return dt.tv_sec + 1e-9*dt.tv_nsec; +} + + +int RetainSaveNeeded(void) +{ + int ret = 0; + static IEC_TIME last_save; + IEC_TIME now; + double diff_s; + + /* no retain */ + if (!retain_info.retain_size) + return 0; + + /* periodic retain flush to avoid high I/O load */ + PLC_GetTime(&now); + + diff_s = CalcDiffSeconds(&now, &last_save); + + if ((diff_s > FILE_RETAIN_SAVE_PERIOD_S) || ForceSaveRetainReq()) { + ret = 1; + last_save = now; + } + return ret; +} + +void ValidateRetainBuffer(void) +{ + if (!retain_buffer) + return; + + /* 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. */ +#ifdef __WIN32 + fflush(retain_buffer); +#else + fsync(fileno(retain_buffer)); +#endif + + fclose(retain_buffer); + retain_buffer = NULL; +} + +void InValidateRetainBuffer(void) +{ + if (!RetainSaveNeeded()) + return; + + /* 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+"); + if (!retain_buffer) { + fprintf(stderr, "Failed to create retain file : %s\n", rb_file); + return; + } + + /* 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) +{ + if (!retain_buffer) + return; + + /* 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); +} +#endif // !HAVE_RETAIN