# HG changeset patch # User Edouard Tisserant # Date 1677593323 -3600 # Node ID 9ce94d0d3764b4046d0d375ae8ffd5dc1d802260 # Parent 87dac789bdaf5be918c2e3f1d75ec7d40dce8440# Parent 0c06de0a39b53dcebcc5d24319df4f08171af8f6 Merged from default diff -r 87dac789bdaf -r 9ce94d0d3764 Beremiz_service.py --- a/Beremiz_service.py Tue Feb 28 15:06:13 2023 +0100 +++ b/Beremiz_service.py Tue Feb 28 15:08:43 2023 +0100 @@ -186,6 +186,12 @@ return os.path.join(beremiz_dir, *args) +import locale +# Matiec's standard library relies on libC's locale-dependent +# string to/from number convertions, but IEC-61131 counts +# on '.' for decimal point. Therefore locale is reset to "C" */ +locale.setlocale(locale.LC_NUMERIC, "C") + def SetupI18n(): # Get folder containing translation files localedir = os.path.join(beremiz_dir, "locale") @@ -206,7 +212,6 @@ # Define locale domain loc.AddCatalog(domain) - import locale global default_locale default_locale = locale.getdefaultlocale()[1] diff -r 87dac789bdaf -r 9ce94d0d3764 modbus/mb_runtime.c --- a/modbus/mb_runtime.c Tue Feb 28 15:06:13 2023 +0100 +++ b/modbus/mb_runtime.c Tue Feb 28 15:08:43 2023 +0100 @@ -373,6 +373,14 @@ clock_gettime(CLOCK_MONOTONIC, &cur_time); fprintf(stderr, "Modbus client thread (%%d) - new cycle (%%ld:%%ld)!\n", client_node_id, cur_time.tv_sec, cur_time.tv_nsec); */ + + /* Variable use to specify delay to introduce between any two consecutive requests we send out to the same client + * Initially set to 0 since we don't want to introduce a delay before the very first request. + */ + struct timespec inter_request_delay; + inter_request_delay.tv_sec = 0; + inter_request_delay.tv_nsec = 0; + int req; for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){ /* just do the requests belonging to the client */ @@ -392,6 +400,14 @@ fprintf(stderr, "Modbus client thread (%%d): RUNNING Modbus request %%d (periodic = %%d flag_exec_req = %%d)\n", client_node_id, req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req ); */ + + /* Insert a delay between any two consecutive requests to the same client + * Needed because some clients will ignore our requests if we send them out too fast. + * + * Note that since we don't want to insert a delay before the very first request we will send, the inter_request_delay variable + * is first initialised to 0. It will be set to the correct delay after the first (and second, third, etc..) request has completed. + */ + clock_nanosleep(CLOCK_MONOTONIC, 0 /* relative sleep */, &inter_request_delay, NULL); int res_tmp = __execute_mb_request(req); client_requests[req].tn_error_code = 0; // assume success @@ -449,7 +465,7 @@ client_requests[req].flag_mb_error_code = client_requests[req].mb_error_code; client_requests[req].flag_tn_error_code = client_requests[req].tn_error_code; - /* We have just finished excuting a client transcation request. + /* We have just finished executing a client transaction request. * If the current cycle was activated by user request we reset the flag used to ask to run it */ if (0 != client_requests[req].flag_exec_req) { @@ -457,6 +473,12 @@ client_requests[req].flag_exec_started = 0; } + /* We have just finished executing a client transaction request. + * Set the inter request delay before we send the next request. Value of delay is set by user in beremiz GUI + */ + inter_request_delay.tv_sec = client_nodes[client_node_id].req_delay / 1000; /* ms to seconds */ + inter_request_delay.tv_nsec = (client_nodes[client_node_id].req_delay %% 1000) * 1000 * 1000; /* ms to ns */ + //fprintf(stderr, "Modbus plugin: RUNNING<---> of Modbus request %%d (periodic = %%d flag_exec_req = %%d)\n", // req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req ); } @@ -956,7 +978,7 @@ * running (i.e. before or after __init_() ets called)! * * The following functions are never called from other C code. They are - * called instead from the python code in runtime/Modbus_config.py, that + * called instead from the python code in modbus/web_settings.py, that * implements the web server extension for configuring Modbus parameters. */ @@ -974,7 +996,7 @@ -/* NOTE: We could have the python code in runtime/Modbus_config.py +/* NOTE: We could have the python code in modbus/web_settings.py * directly access the server_node_t and client_node_t structures, * however this would create a tight coupling between these two * disjoint pieces of code. @@ -1017,6 +1039,7 @@ int __modbus_get_ClientNode_parity (int nodeid) {return client_nodes[nodeid].node_address.addr.rtu.parity; } int __modbus_get_ClientNode_stop_bits (int nodeid) {return client_nodes[nodeid].node_address.addr.rtu.stop_bits;} u64 __modbus_get_ClientNode_comm_period(int nodeid) {return client_nodes[nodeid].comm_period; } +u64 __modbus_get_ClientNode_req_delay (int nodeid) {return client_nodes[nodeid].req_delay; } const char * __modbus_get_ClientNode_addr_type (int nodeid) {return addr_type_str[client_nodes[nodeid].node_address.naf];} const char * __modbus_get_ServerNode_config_name(int nodeid) {return server_nodes[nodeid].config_name; } @@ -1037,6 +1060,7 @@ void __modbus_set_ClientNode_parity (int nodeid, int value) {client_nodes[nodeid].node_address.addr.rtu.parity = value;} void __modbus_set_ClientNode_stop_bits (int nodeid, int value) {client_nodes[nodeid].node_address.addr.rtu.stop_bits = value;} void __modbus_set_ClientNode_comm_period(int nodeid, u64 value) {client_nodes[nodeid].comm_period = value;} +void __modbus_set_ClientNode_req_delay (int nodeid, u64 value) {client_nodes[nodeid].req_delay = value;} void __modbus_set_ServerNode_host (int nodeid, const char * value) {if (strcmp(value,"#ANY#")==0) value = ""; diff -r 87dac789bdaf -r 9ce94d0d3764 modbus/mb_runtime.h --- a/modbus/mb_runtime.h Tue Feb 28 15:06:13 2023 +0100 +++ b/modbus/mb_runtime.h Tue Feb 28 15:08:43 2023 +0100 @@ -124,7 +124,8 @@ node_addr_t node_address; int mb_nd; // modbus library node used for this client int init_state; // store how far along the client's initialization has progressed - u64 comm_period;// period to use when periodically sending requests to remote server + u64 comm_period;// period to use when periodically sending requests to remote server (in ms) + u64 req_delay; // delay between 2 consecutive requests sent to remote slaves/server (in ms) int prev_error; // error code of the last printed error message (0 when no error) pthread_t thread_id; // thread handling all communication for this client node pthread_t timer_thread_id; // thread handling periodical timer for this client node diff -r 87dac789bdaf -r 9ce94d0d3764 modbus/mb_utils.py --- a/modbus/mb_utils.py Tue Feb 28 15:06:13 2023 +0100 +++ b/modbus/mb_utils.py Tue Feb 28 15:08:43 2023 +0100 @@ -140,10 +140,10 @@ params: child - the correspondent subplugin in Beremiz """ node_init_template = '''/*node %(locnodestr)s*/ -{"%(locnodestr)s", "%(config_name)s", "%(device)s", "", {naf_rtu, {.rtu = {NULL, %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */}''' - - location = ".".join(map(str, child.GetCurrentLocation())) - config_name, device, baud, parity, stopbits, coms_period = GetCTVals(child, range(6)) +{"%(locnodestr)s", "%(config_name)s", "%(device)s", "", {naf_rtu, {.rtu = {NULL, %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period (ms)*/, %(coms_delay)s /* inter request delay (ms)*/, 0 /* prev_error */}''' + + location = ".".join(map(str, child.GetCurrentLocation())) + config_name, device, baud, parity, stopbits, coms_period, coms_delay = GetCTVals(child, range(7)) node_dict = {"locnodestr": location, "config_name": config_name, @@ -151,7 +151,8 @@ "baud": baud, "parity": modbus_serial_parity_dict[parity], "stopbits": stopbits, - "coms_period": coms_period} + "coms_period": coms_period, + "coms_delay": coms_delay} return node_init_template % node_dict @@ -161,16 +162,17 @@ params: child - the correspondent subplugin in Beremiz """ node_init_template = '''/*node %(locnodestr)s*/ -{"%(locnodestr)s", "%(config_name)s", "%(host)s", "%(port)s", {naf_tcp, {.tcp = {NULL, NULL, DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */, 0 /* prev_error */}''' - - location = ".".join(map(str, child.GetCurrentLocation())) - config_name, host, port, coms_period = GetCTVals(child, range(4)) +{"%(locnodestr)s", "%(config_name)s", "%(host)s", "%(port)s", {naf_tcp, {.tcp = {NULL, NULL, DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period (ms)*/, %(coms_delay)s /* inter request delay (ms)*/, 0 /* prev_error */}''' + + location = ".".join(map(str, child.GetCurrentLocation())) + config_name, host, port, coms_period, coms_delay = GetCTVals(child, range(5)) node_dict = {"locnodestr": location, "config_name": config_name, "host": host, "port": port, - "coms_period": coms_period} + "coms_period": coms_period, + "coms_delay": coms_delay} return node_init_template % node_dict diff -r 87dac789bdaf -r 9ce94d0d3764 modbus/modbus.py --- a/modbus/modbus.py Tue Feb 28 15:06:13 2023 +0100 +++ b/modbus/modbus.py Tue Feb 28 15:08:43 2023 +0100 @@ -319,11 +319,19 @@ + + + + + + + + """ - # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms + # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms and Request_Delay_in_ms # corresponds to aprox 25 days. CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] # TODO: Replace with CTNType !!! @@ -565,11 +573,19 @@ + + + + + + + + """ - # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms + # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms and Request_Delay_in_ms # corresponds to aprox 25 days. CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] # TODO: Replace with CTNType !!! diff -r 87dac789bdaf -r 9ce94d0d3764 modbus/web_settings.py --- a/modbus/web_settings.py Tue Feb 28 15:06:13 2023 +0100 +++ b/modbus/web_settings.py Tue Feb 28 15:08:43 2023 +0100 @@ -149,7 +149,8 @@ # annotate.Integer, ...) ("host" , _("Remote IP Address") , ctypes.c_char_p, MB_StrippedString), ("port" , _("Remote Port Number") , ctypes.c_char_p, MB_StrippedString), - ("comm_period" , _("Invocation Rate (ms)") , ctypes.c_ulonglong, annotate.Integer ) + ("comm_period" , _("Invocation Rate (ms)") , ctypes.c_ulonglong, annotate.Integer ), + ("req_delay" , _("Request Delay (ms)") , ctypes.c_ulonglong, annotate.Integer ) ] RTUclient_parameters = [ @@ -161,7 +162,8 @@ ("baud" , _("Baud Rate") , ctypes.c_int, MB_Baud ), ("parity" , _("Parity") , ctypes.c_int, MB_Parity ), ("stop_bits" , _("Stop Bits") , ctypes.c_int, MB_StopBits ), - ("comm_period" , _("Invocation Rate (ms)") , ctypes.c_ulonglong, annotate.Integer) + ("comm_period" , _("Invocation Rate (ms)") , ctypes.c_ulonglong, annotate.Integer), + ("req_delay" , _("Request Delay (ms)") , ctypes.c_ulonglong, annotate.Integer) ] TCPserver_parameters = [ diff -r 87dac789bdaf -r 9ce94d0d3764 svghmi/widgetlib/bool_indicator.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widgetlib/bool_indicator.svg Tue Feb 28 15:08:43 2023 +0100 @@ -0,0 +1,82 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -r 87dac789bdaf -r 9ce94d0d3764 svghmi/widgetlib/simple_text_display.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widgetlib/simple_text_display.svg Tue Feb 28 15:08:43 2023 +0100 @@ -0,0 +1,64 @@ + + + + + + + + + + image/svg+xml + + + + + + value + diff -r 87dac789bdaf -r 9ce94d0d3764 targets/Linux/XSD --- a/targets/Linux/XSD Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/Linux/XSD Tue Feb 28 15:08:43 2023 +0100 @@ -1,6 +1,7 @@ + %(toolchain_gcc)s - \ No newline at end of file + diff -r 87dac789bdaf -r 9ce94d0d3764 targets/Linux/__init__.py --- a/targets/Linux/__init__.py Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/Linux/__init__.py Tue Feb 28 15:08:43 2023 +0100 @@ -32,7 +32,11 @@ extension = ".so" def getBuilderCFLAGS(self): - return toolchain_gcc.getBuilderCFLAGS(self) + ["-fPIC"] + additional_cflags = ["-fPIC"] + build_for_realtime = self.CTRInstance.GetTarget().getcontent().getRealTime() + if build_for_realtime: + additional_cflags.append("-DREALTIME_LINUX") + return toolchain_gcc.getBuilderCFLAGS(self) + additional_cflags def getBuilderLDFLAGS(self): return toolchain_gcc.getBuilderLDFLAGS(self) + ["-shared", "-lrt"] diff -r 87dac789bdaf -r 9ce94d0d3764 targets/Linux/plc_Linux_main.c --- a/targets/Linux/plc_Linux_main.c Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/Linux/plc_Linux_main.c Tue Feb 28 15:08:43 2023 +0100 @@ -11,6 +11,9 @@ #include #include #include +#ifdef REALTIME_LINUX +#include +#endif static unsigned long __debug_tick; @@ -105,10 +108,59 @@ pthread_exit(0); } +#define _LogError(text,...) \ + {\ + char mstr[256];\ + snprintf(mstr, 255, text, ##__VA_ARGS__);\ + LogMessage(LOG_CRITICAL, mstr, strlen(mstr));\ + } #define maxval(a,b) ((a>b)?a:b) int startPLC(int argc,char **argv) { - setlocale(LC_NUMERIC, "C"); + + int ret; + pthread_attr_t *pattr = NULL; + +#ifdef REALTIME_LINUX + struct sched_param param; + pthread_attr_t attr; + + /* Lock memory */ + ret = mlockall(MCL_CURRENT|MCL_FUTURE); + if(ret == -1) { + _LogError("mlockall failed: %m\n"); + return ret; + } + + /* Initialize pthread attributes (default values) */ + ret = pthread_attr_init(&attr); + if (ret) { + _LogError("init pthread attributes failed\n"); + return ret; + } + + /* Set scheduler policy and priority of pthread */ + ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + if (ret) { + _LogError("pthread setschedpolicy failed\n"); + return ret; + } + param.sched_priority = PLC_THREAD_PRIORITY; + ret = pthread_attr_setschedparam(&attr, ¶m); + if (ret) { + _LogError("pthread setschedparam failed\n"); + return ret; + } + + /* Use scheduling parameters of attr */ + ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if (ret) { + _LogError("pthread setinheritsched failed\n"); + return ret; + } + + pattr = &attr; +#endif PLC_shutdown = 0; @@ -120,7 +172,7 @@ pthread_mutex_lock(&debug_wait_mutex); pthread_mutex_lock(&python_wait_mutex); - if( __init(argc,argv) == 0 ){ + if((ret = __init(argc,argv)) == 0 ){ /* Signal to wakeup PLC thread when period changes */ signal(SIGUSR1, PLCThreadSignalHandler); @@ -133,9 +185,13 @@ period_ns = common_ticktime__; clock_gettime(CLOCK_MONOTONIC, &next_abs_time); - pthread_create(&PLC_thread, NULL, (void*) &PLC_thread_proc, NULL); + ret = pthread_create(&PLC_thread, pattr, (void*) &PLC_thread_proc, NULL); + if (ret) { + _LogError("create pthread failed\n"); + return ret; + } }else{ - return 1; + return ret; } return 0; } diff -r 87dac789bdaf -r 9ce94d0d3764 targets/Win32/plc_Win32_main.c --- a/targets/Win32/plc_Win32_main.c Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/Win32/plc_Win32_main.c Tue Feb 28 15:08:43 2023 +0100 @@ -82,7 +82,6 @@ { unsigned long thread_id = 0; BOOL tmp; - setlocale(LC_NUMERIC, "C"); debug_sem = CreateSemaphore( NULL, // default security attributes diff -r 87dac789bdaf -r 9ce94d0d3764 targets/beremiz.h --- a/targets/beremiz.h Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/beremiz.h Tue Feb 28 15:08:43 2023 +0100 @@ -32,4 +32,13 @@ int unblock_RT_to_nRT_signal(void* handle); void nRT_reschedule(void); + +#ifdef REALTIME_LINUX + +#ifndef PLC_THREAD_PRIORITY +#define PLC_THREAD_PRIORITY 80 #endif + +#endif + +#endif diff -r 87dac789bdaf -r 9ce94d0d3764 targets/plc_debug.c --- a/targets/plc_debug.c Tue Feb 28 15:06:13 2023 +0100 +++ b/targets/plc_debug.c Tue Feb 28 15:08:43 2023 +0100 @@ -270,7 +270,7 @@ default: break; } - force_list_apply_cursor++; \ + force_list_apply_cursor++; } /* Reset buffer cursor */