--- 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<count; position++ ){
- printf("%d : 0x%2.2x\n", offset+position, ((char*)p)[position]);
- }
- */
-}
-
-void Remind(unsigned int offset, unsigned int count, void *p)
-{
-}
-#endif // !HAVE_RETAIN
-
static void __attribute__((constructor))
beremiz_dll_init(void)
{
--- /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 @@
+#ifndef HAVE_RETAIN
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#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