No idea if it is ok with CFC, but with ST/LD/SFC you can split your code in different routines and then call each one when needed (for exemple, do a Main PRG, and call conditionnaly every other PRG from there).
Then just use an external length variable. For instance, your array max length is 10. If you only fill it with 5 items, then reset lefts items (5..9) and set your length variable to 5. Edit : This is how string are handled (a byte array with a max length as defined in the variable definition and then the actual length is set by the first null byte)
Then just use an external length variable. For instance, your array max length is 10. If you only fill it with 5 items, then reset lefts items (5..9) and set your length variable to 5.
So you are basically doing what mqtt broker does ? Persistent values are often stored in a file. File permissions are often locked for read only, explaining your troubles. To store values in a file that may be emptied by other thread, I would buffer some values in memory and write once the buffer is almost full. Then, as the file is no longer used, you may empty it. You can also empty your buffer on a trigger, in case of power loss for exemple. Third solution, a bit more complex, may be to reread...
Hum, regular way to do this is using systime library. You need to add it to your project in the library manager. Then use SysTimeGetMs to get actual CPU time in milliseconds ( ! This time is UTC time, not localized). Then, just substract values to get the delta time.
Common approach may be to size your array to the maximum size (if size is not a problem, ie on small arrays), then to use a key value to detect array end (ie nullbyte 16#00). This way you can fill your dut array with values and optionnaly indicate the end of dut array. This approach is valid only with small arrays, as reserved memory may be an issue.
For this purpose, if you run it on a simulator, then cheat engine may help with it's build in speedhack. Just select the simulator process and set the speedhack flag. Other may may be generating the actual time from counters and passing it to your function instead of the actual rtc time.
I'm running 3.5 SP12 and I can't open your project. If you are using a slave device, then just make sure the refresh task is correctly set (bus cycle task). Still, the addressing offsets may apply. Maybe your troubles are related to online edits and are solved with a download ? This could be pointers related stuff.
SFC is not the language I would choose for processing any communication like modbusTCP is. Correct me if I'm wrong: the Controlwin PLC is the modbusTCP slave (so, the server awainting for client to connect and issue requests). You are trying to read 2 bytes, namely %IB0 and %IB1, and to write 1 other (%QB0). First of all, be aware that modbus is a 16bit (word) oriented protocol. So, writing a byte is totally wrong. You are either writing some coils (bits), one by one, or writing a multiple of words...
Did you had a look at CAA Net Base Services ? There's some UDP server that may help. Also have a look in codesys store for exemple project. https://help.codesys.com/webapp/idx-CAA_NetBaseServices-lib;product=CAA_NetBaseServices;version=3.5.9.50
Did you had a look at CAA Net Base Services ? https://help.codesys.com/webapp/idx-CAA_NetBaseServices-lib;product=CAA_NetBaseServices;version=3.5.9.50
Wikipedia says : In computer networking, a port is a communication endpoint. At the software level, within an operating system, a port is a logical construct that identifies a specific process or a type of network service. This means that a port is more or less an ID. So you can't share the same port between two different software on the same machine. You should be able to change OPCua port in your controller OPCua settings.
Reading how SFC handles transitions and how step actions are performed in the help is quite a mandatory step. At least one scan cycle time, plus every seconds it takes for your previous exit action and next entry action and next continuous action to be performed.
Just read how SFC handles transitions and how step actions are performed in the help. At least one scan cycle time, plus every seconds it takes for your previous exit action and next entry action and next continuous action to be performed.
Using an inout variable will suit your requirements. More generally, if you need to know the previous state of an output variable, then it should be an inout. Inout refers to variables you want to read and write. Just define the inout variable as persistent.
What about this : This will introduce some filtering on your value. rActualPower := rActualPower * 0.99 + rPidOutputPower * 0.01; You may also do this, but it will surely oscillate and tends not to reflect any real system: aRrBuffer : ARRAY 1..100 OF REAL; --- 'Cycle index value index := index + 1; If index > 100 Then index := 1; End_If 'Recover stored value rPidInputPower := aRrBuffer[index]; 'Place new command in buffer aRrBuffer[index] := rPidOutputPower; Both examples need to be called in a cyclical...
If watchdog triggers, then you may have an issue, setting up watchdog time or in your code. I recommend you to investigate your issue (what task failed watchdog ? Do your normal cycle time is near watchdog time ? Do you have loops with bad exit conditions ?) Watchdog is not a mechanism designed to restart plc, but to stop it and prevent machine/operator damage.
I see two ways doing this. First one, declare your fb instances, then directly map fb inputs bit by bit. Exemple, Lets say you have an alarm status input that is fbPump01.bAlm. Just declare the fb as Pump01, and add an input as bAlm. Then map fbPump01.bAlm to the right input bit. Second one, if you need dynamic addressing, just define an array on your I/Os. Then use a pointer to get the starting address I/O ADR(arBDigInputs). Last, inside the fb, using a slice input parameter, move your pointer to...
I see two ways doing this. First one, declare your fb instances, then directly map fb inputs bit by bit. Exemple, Lets say you have an alarm status input that is fbPump01.bAlm. Just declare the fb as Pump01, and add an input as bAlm. Then map fbPump01.bAlm to the right input bit. Second one, if you need dynamic addressing, just define an array on your I/Os. Then use a pointer to get the starting address I/O ADR(arBDigInputs). Last, inside the fb, using a slice input parameter, move your pointer to...
Had a look at OSCAT basic, which is implementing quicksort in ARRAYSORT (REAL datatype only). it's faster on simulation, sorting 10000 random items is averaging 1ms while my code produces 7ms. Maybe that's due too the good use of cpu caching by C&D. That would be great to have some measurements on a real PLC. Or maybe that's worthless reinventing what's already written down π
Had a look at OSCAT basic, which is implementing quicksort in ARRAYSORT (REAL datatype only). it's a faster on simulation, sorting 10000 random items is averaging 1ms while my code produces 7ms. Maybe that's due too the good use of cpu caching by C&D. That would be great to have so measurement on a real PLC. Or maybe that's worthless reinventing what's already written down π
@nothinrandom Fixed code with some cleanup. Feel free to add it to your base. FUNCTION numInsertSort : BOOL VAR_INPUT pbBuffer : POINTER TO BYTE; // pointer to buffer diElements : DINT; // array length uiDataType : UINT; // data type: SINT(1), USINT/BYTE(2), INT(3), UINT/WORD(4), DINT(5), UDINT/DWORD(6), LINT(7), ULINT/LWORD(8), REAL(9), LREAL(10) bOrder : BOOL := FALSE; // default ascending END_VAR VAR _uiDataSize : UINT; // element size _diIndex1 : DINT; // loop index _diMaxIndex : DINT; // loop...
@nothinrandom(https://forge.codesys.com/u/nothinrandom/) Fixed code with some cleanup. Feel free to add it to your base. FUNCTION numInsertSort : BOOL VAR_INPUT pbBuffer : POINTER TO BYTE; // pointer to buffer diElements : DINT; // array length uiDataType : UINT; // data type: SINT(1), USINT/BYTE(2), INT(3), UINT/WORD(4), DINT(5), UDINT/DWORD(6), LINT(7), ULINT/LWORD(8), REAL(9), LREAL(10) bOrder : BOOL := FALSE; // default ascending END_VAR VAR _uiDataSize : UINT; // element size _diIndex1 : DINT;...
Well, the first index is not treated by the loop, as it starts at 1, but should be when we try to place the second. I will have a look later on to correct it. This may be something like divide, but to be honest, it is far from what quick sort could do. The problem is, we can't do recursive calls in codesys (afaik) and thus we have to maintain an explicit call stack and external parameters, which is yet more complexity and more overhead. In fact my approach, was to port an old program I did years...
This a dirty example, tested with reals. The purpose is to skip some items testing. This is an insertion sort, and it is expected to be ineffective on small arrays. But on large ones, it should almost square root the processing time. I've did some modifications to your code template because of unsigned convertion warnings (on 3.5.12.6). FUNCTION numInsertSort : BOOL VAR_INPUT pbBuffer : POINTER TO BYTE; // pointer to buffer udiElements : UDINT; // array length uiDataType : UINT; // data type: SINT(1),...
This a dirty example, tested with reals. The purpose is to skip some items testing. This is an insertion sort, and it is expected to be ineffective on small arrays. But on large ones, it should almost square root the processing time. I've did some modifications to your code template because of unsigned conversion warnings (on 3.5.12.6). FUNCTION numInsertSort : BOOL VAR_INPUT pbBuffer : POINTER TO BYTE; // pointer to buffer udiElements : UDINT; // array length uiDataType : UINT; // data type: SINT(1),...
first of all, don't try to place the inserted element at the end, but test at the actual halved length element. If it is lower and you are going for ascending, then add the halved remaining number of elements to the top, and repeat. Repeat whole till the last lower checked element and the last upper checked element are contiguous.
Great work. Could you imagine adding some index optimization ? If elements are swapped, then approximate the new index by halving the distance to the max possible (needs two index memory more). This is a pretty good optimization in case of large arrays, almost square rooting the number of tests needed to insert correctly. Anyway, Kudos to you.
If sources weren't sent into your PLC, then you can only recover binaries, which is only useful as a backup. You can later use this backup to download on a fresh new PLC, but you can't edit or modify it with codesys.
Please provide ST_3bit_7bit code (copy/paste issue). Providing the expected bit partern over a word would also help.
Please provide ST_3bit_7bit code (copy/paste issue)
For existing libraries you have no choice but to use the full qualified namespace (or shortcut name).
In automation, when you use a variable, you use a value. This value is never updated when you reads it. This behavior is due to how the code is executed at runtime. It is executed line by line, from the first one to the last one, and (in general) restart from the first. So for a timer (which is a function block updating some values), to get your outputs updated, you need to call the instance writing the outputs.
what about : https://store.codesys.com/xml-utility-bundle.html ?
In case you missed it, what the thread autor is discovering is the general behavior of any functionblock in automation (from codesys or other provider). To update a function block output, you have to call its instance.
To me it feels wrong using multiple instances. RTU aim one com port, so there is only one query at a time. Using only one instance is the guarantee you have only one running at a time. Just update the pointers and sizes to point to the query you want to execute. Cycle 1 : execute=true, instance call Cycle 2 : if xDone, execute=false, instance call, update pointers, execute=true, instance call Cycle n : same as cycle 2.
You may need a cycle with execute=false (or two successive calls) because queries are send with raising edge of execute. A simple case may be appropriate to handle the queue/active queries, and one more to correctly sequence your queries.
Do you suceed with multiples different queries to the same slave ? Do you correctly wait for the xDone=TRUE plus a scan cycle with xExecute=FALSE ?
This may help : https://help.codesys.com/webapp/_mod_lib_modbusrequest;product=core_modbus_configuration_editor;version=3.5.15.0
If you are interested into optimizing the sort, then, this is how .net framework handle unidimensional array sorting : If the partition size is less than or equal to 16 elements, it uses an insertion sort algorithm. If the number of partitions exceeds 2 * LogN, where N is the range of the input array, it uses a Heapsort algorithm. Otherwise, it uses a Quicksort algorithm Source : https://docs.microsoft.com/en-us/dotnet/api/system.array.sort?view=netframework-4.8#System_Array_Sort_System_Array_