Search talk: method

 
<< < 1 .. 5 6 7 8 > >> (Page 7 of 8)

Post by julianramirez on ModbusFB write update CODESYS Forge talk (Post)
Hello everyone, I am testing the ModbusFB library tcp server and so far I am able to create holding registers successfully, however, I am trying to identify after each write which registers got updated (i.e. function code, write value). I can even see the var udiNumWriteRequests, which increases with every write. I noticed that there is logging with the LogStatusInfo method. After I call it I am able to read in the console stuff that I want. Nevertheless, this is only available at the logs and is not easy to decode because it consists on several messages, I would like to know if there is a way for me to retrieve this information from the function itself with pointers or if there is any way to copy the logs messages (assuming that I can filter them with the LoggingOptions to only show what I need) inside the runtime code and not in the console. Thanks for your help :)
Last updated: 2024-09-16

Post by imdatatas on Read "Homed" status from EtherCAT drive CODESYS Forge talk (Post)
Hello, I have experienced such situations with some servo drive devices in the past. The home completion information is reported to the upper controller by the slave drive with the states of the relevant bits in the StatusWord (for example; 6041h Bit10, Bit12 in Home mode). If the states of these bits are correct in terms of number and state but the motor does not actually perform the action; You can contact the slave drive manufacturer. There may be a problem with the device firmware. Another area you can play around with is looking at the settings in the "Advanced configuration" for Softmotion axis on the page the link to below, and trying it out and using the one that works for you. https://content.helpme-codesys.com/en/CODESYS%20SoftMotion/_sm_cmd_add_softmotion_cia402_axis.html A method I recommended worked for a user as in below message. https://forge.codesys.com/forge/talk/Engineering/thread/40bc82454d/ I hope your problem will be solved as soon as possible and by sharing the result here, you will help others to know and overcome it. Good luck. Best regards, Imdat
Last updated: 2025-01-08

Post by pouyavakili on Execute Codesys Tests with python CODESYS Forge talk (Post)
❓ Issue: TestManager Not Available in Python Script (AttributeError) 🙏 Question: Why does project.get_testmanager() not work in scripting, even though Test Manager is installed and licensed, and the script runs in GUI mode? Is there a way to ensure the Test Manager interface is loaded or accessible from the scripting environment? 🧠 Summary: I'm trying to automate test execution using the CODESYS scripting engine (Python) via a script that opens a project, builds the application, and runs tests from a TestRepository.xml. Although the Test Manager works manually in the IDE, it fails when accessed via script with the following error: AttributeError: 'ScriptObject' object has no attribute 'testmanager' 🧪 Test Setup: CODESYS version: 3.5.21.10 Test Manager: Installed and licensed (works in the IDE manually) Project: Contains a test repository, which can be executed via the GUI Script execution method: powershell Start-Process -FilePath "C:\Program Files\CODESYS 3.5.21.10\CODESYS\Common\CODESYS.exe" ` -ArgumentList @( '--profile="CODESYS V3.5 SP21 Patch 1"', '--runscript="C:\Scripts\run_tests.py"' ) -Wait 📜 Script Summary: python project = projects.open(project_path) application = project.active_application application.build() This fails: testmanager = project.get_testmanager() ❗ Problem: Calling project.get_testmanager() or application.testmanager throws an AttributeError, even though the same project and test repository work in the GUI.
Last updated: 2025-07-23

Post by imdatatas on MC_MoveAbsolute: C0138: No matching 'FB_Init' method found for instantiation of SMC_MoveAbsRelPerformerImpl [ERROR] CODESYS Forge talk (Post)
Dear @gseidel, Thank you for your reply. The exact version of Codesys and its compiler was 3.5.21.20. The compiler version was fixed and could not be changed in project settings. Since I couldn't continue due to this compilation error, I temporarily overcame this issue as follows. -- I completely uninstalled Codesys 3.5.21.20 and completely deleted the Codesys folder in the C:\ProgramData folder. -- I restarted my computer and installed Codesys 3.5.19.70. -- I updated the Codesys Installer and all its add-ons to the latest versions. -- The project that was giving me the compilation error I mentioned before is now open with Codesys 3.5.19.70, compiled it, and successfully downloaded it on the device. -- I haven't encountered the same problem anymore. I think this issue is related to SP21 and its compiler. Frankly, I'd expect a project created with an older version to compile without errors after opening it with the latest Codesys version and updating all libraries. Regards, imdatatas
Last updated: 2025-09-26

Post by axilleas on SMC_Interpolator + SMC_controAxislbyPos CODESYS Forge talk (Post)
dear all I am working on a very generic gantry system (virtual drives + win v3 control) reading gcode from external file. In order to stop the interpolator function from running when gaps are detected, SMC_controlAxisByPos FB has the bStopIpo output which according to the manual is connected to the Interpolator's bEmergencyStop. Lets assume that the axis are parked in position X1000 Y1000 and the initial G code movement is G00 X500 Y500. The normal behavior is to travel from the actual x1000 y1000 to G00 X500 Y500. The SMC_Interpolator always gets the X0 Y0 as the initial position, meaning that the bStopIpo is active until the axis are in X0 Y0 and the release the interpolator to set the new positions to X500 Y500. Is there a suggested method to solve this behavior? bonus question: I get a lot of bStopIpo during standard interpolation movements (not gaps). While working with virtual axis this is not a problem but when real axis are installed this will be an issue. What exactly triggers the bStopIpo output? Slow axis accelerations compare to Gcode defaults? Kind Regards
Last updated: 2024-02-05

Post by mxj262 on FB having single input but initialized with Array CODESYS Forge talk (Post)
I am adding elements of an ARRAY using pointer to access each element inside a FOR loop and the FOR loop does not stop! What is the right way to use pointers in such case?? I have another loop that is not using pointer and it stops but the loop using pointer keep on adding. METHOD FB_Init: BOOL VAR_INPUT bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold) bInCopyCode: BOOL; // TRUE: the instance will be copied to the copy code afterward (online change) END_VAR VAR_IN_OUT // basically REFERENCE TO window_buffer: ARRAY [*] OF INT; // array of any size END_VAR THIS^.windowPtr := ADR(window_buffer[0]); THIS^.windowSize := UPPER_BOUND(window_buffer, 1) - LOWER_BOUND(window_buffer, 1) + 1; FUNCTION_BLOCK FB500 VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR windowPtr: POINTER TO INT; windowSize: DINT; currentIndex: UINT; element1:INT; element2:INT; i:INT; j:INT; sum:DINT:=0; END_VAR element1:=windowPtr[0]; // read the first element of the Array dynamic memorry element2:=windowPtr[1]; FOR i:=0 TO (TO_INT(windowSize-1)) BY 1 DO // this loop does not stop Sum:=sum + windowPtr[i]; END_FOR FOR j:=0 TO 5 BY 1 DO // this loop stops j:=j+1; END_FOR https://ibb.co/k3DhkZT
Last updated: 2024-05-06

Post by rabaggett on Reading Named Pipes in Linux Is there a better way? CODESYS Forge talk (Post)
I have a Python program that will handle things in my application such as VISA over IP and Telnet control of instruments. I want the main control and HMI in Codesys. My problem is communication between the two. I have looked at several ways to accomplish this, and settled on having the Python program create two named pipes, one to send information to Codesys, and one for Codesys to send information to Python. The information would be packetized with \n for end of packet.. Seems simple. I think the named pipes method should work, but file reading in Codesys using the CAA file library starts to get difficult when the file is never ending, as in this case. Before I spend too much time making something that may be fundamentally flawed I want to ask. Is there a better way? Is there a way to read the file 'one line at a time' which might solve my never ending file problem? Thanks!
Last updated: 2024-05-09

Post by william-blandon on RecipeManCommands.ReloadRecipes CODESYS Forge talk (Post)
Hi Dave. Thanks for your answer. I still struggling with this. I have done everything according with the Codesys help even I have looked in detail at the example. I have double checked the recipe manager configuration and the use of all the methods. There are two methods that do not work as expected. I'm using Codesys 3.5.19.20 and recipe management 4.4.0.0 .CreateRecipe just create a recipe in the recipe definition but do not create the file. This have a walk around just giving a .ReadAndSaveRecipe afterwards. .ReloadRecipes do nothing but do not generate any error as well. After calling it I call the .GetRecipeCount and the number is always 0. When I create a new recipe (using the function block methods) then the number changes. Looks like this method is looking to the wrong directory finding nothing. I don't know if there is a missing configuration in the CODESYSControl.cfg file. I'm thinking in a walk around for this but I need to create a strings array with the recipe names as retentive and making all the management when deleting and adding recipes. Have had some one the same problem?
Last updated: 2024-05-23

Post by sigurdrb on send appliaction files to wago plc (codesys 3.5) via ssh CODESYS Forge talk (Post)
Hi, I was hoping I could send the two files "application.app" and "application.crc" directly to the directory "/home/codesys/PlcLogic/Application" instead of logging into the device with codesys, downloading the program to the controller and then do the "create boot application", so it will reboot with the correct program running. I think i also need to edit the file "eRUNTIME.cfg" located in the "/home/codesys/ folder, because when I create the boot application from the codesys IDE, these lines are added to the file: [CmpApp] Application.1 = Application [SysFileMap] /home/codesys/PlcLogic/Application/Application.app=0x212520, 0x2DBC747E, 2DBC747E.app I think the "eRUNTIME.cfg" file is the same as CODESYSControl.cfg I also dont know the purpose of the hexadecimal numbers etc. that is generated. When I tried to replicate what codesys was doing, I placed the same files in the same folder as when codesys generated the boot file. And also added the lines in the eRUNTIME.cfg file: [CmpApp] Application.1 = Application [SysFileMap] /home/codesys/PlcLogic/Application/Application.app The controller did not boot up with any program, so my method did not work at all. I guess these lines tells the controller to run an application and where to find it? Could any of you help me with the best way to get the compiled program to the controller without using codesys or wago-software?
Last updated: 2024-05-28

Post by mondinmr on COL.IMap2 and HashTableFactory cause frequent Access Violation CODESYS Forge talk (Post)
Good morning, I’ve been using the Collection library for a long time, mostly with Stack, LinkedList, and List. From your examples I was able to manage the different factories, even for custom elements. Now I need a hashtable, but with this object I’m running into a lot of random access violations. If I append something inside FB_Init after constructing the hashtable, it crashes, even though the list was already created. If I only create the hashtable in FB_Init and append afterwards, it usually works. I don’t understand the meaning of the dispose call in this code: pSlave := ADR(slave); uSlave := TO_ULINT(pSlave); iKey := fKey.Create(uSlave); IF hash.CountKeys() = 0 THEN Service.logger.appendLog('Filling hashtable', 'HashManager', AdvLogType.AdvDebugMsg); ok := FALSE; ELSE eError := hash.ContainsKey(iKey, xResult => ok); END_IF IF NOT ok THEN pI := __NEW(UINT); iVal := fKey.Create(TO_ULINT(pI)); Service.logger.appendLog(CONCAT('New slave', TO_STRING(uSlave)), 'HashManager', AdvLogType.AdvDebugMsg); hash.AddKeyValuePair(iKey, iVal); appendNewSlave := pI; ELSE eError := hash.GetElementByKey(iKey, itfValue => iElem); xResult := __QUERYINTERFACE(iKey, itfIInstance); IF xResult THEN itfIInstance.Dispose(); END_IF IF eError <> COL.COLLECTION_ERROR.NO_ERROR THEN Service.logger.appendLog(CONCAT('ERROR ', TO_STRING(eError)), 'HashManager', AdvLogType.AdvCriticalMsg); appendNewSlave := nullptr; RETURN; END_IF __QUERYINTERFACE(iElem, iVal); {warning disable C0033} pI := TO___UXINT(iVal.UlintValue); {warning restore C0033} appendNewSlave := pI; END_IF Without the dispose call, every second cold reset crashes immediately when I try to access iVal, even if eError doesn’t report any error. With the dispose call, the cold reset issue disappears, but I get other problems: a) If I start the runtime using systemctl start codesyscontrol, it crashes at IF hash.CountKeys() = 0 THEN b) If I delete the files in PlcLogic and download again, it works and survives multiple cold resets. But as soon as I run systemctl restart codesyscontrol, everything gets corrupted again and it starts crashing at that point. FUNCTION_BLOCK SlaveMapCounter VAR hash : COL.IMap2; eError : COL.COLLECTION_ERROR; END_VAR In its FB_Init I create it: METHOD FB_Init: BOOL VAR_INPUT bInitRetains: BOOL; // TRUE: retain variables are initialized (reset warm / reset cold) bInCopyCode: BOOL; // TRUE: the instance will be copied to the copy code afterward (online change) END_VAR VAR hF : COL.HashTableFactory; END_VAR hash := hF.Create(256); In another FB I instantiate it statically: FUNCTION_BLOCK ABSTRACT AbstractServoEthercatController EXTENDS AbstractServoController VAR_STAT hashSlaves : SlaveMapCounter; END_VAR VAR field : REFERENCE TO ADVAbstractFieldUnitEthercatCia402; initCnt : REFERENCE TO UINT; END_VAR The append method is the one shown above, and I call it after the runtime has started. The accesses are performed by a single task, and in any case I’m working on an isolated single core. I’ve tried everything, moving the create, the instances, and all the rest several times, but nothing seems to work. I’d like to point out that these FBs are part of our own library, which is used in many applications.
Last updated: 2025-09-11

Post by edson-bueno on SysProcess Execute Command unable to run commands with special characters CODESYS Forge talk (Post)
Hi, I found the same issue, and I fixed with this steps: 1st go to codesys .cfg file. sudo nano CODESYSControl.cfg Then insert this: [SysProcess] BasePriority=Realtime Command=AllowAll Now we need to grant codesys root rights on Linux. Step 1: Create or edit the systemd override configuration: sudo systemctl edit codesyscontrol In the editor that opens, insert: [Service] User=root Save and exit: Press Ctrl+O to save Press Ctrl+X to exit Step 2: Reload systemd and reboot To apply the override: sudo systemctl daemon-reexec sudo systemctl daemon-reload sudo reboot Step 3: Confirm CODESYS is running as root After reboot, open the terminal and run: ps aux | grep codesyscontrol You should see something like: root 1234 ... /opt/codesys/bin/codesyscontrol.bin ... If instead it shows admin or another user, the override was not applied correctly. Step 4: (Optional) Confirm from within CODESYS In your CODESYS project, insert this test code to run the Linux command whoami: Make sure the lib SysProcessImplementation, SysTypes, and CmpErrors is on the project. VAR sCommand : STRING := '/usr/bin/whoami'; sOutput : STRING(255); refCommand : REFERENCE TO STRING; refOutput : REFERENCE TO STRING; resultCmd : UDINT; END_VAR refCommand REF= sCommand; refOutput REF= sOutput; SysProcessExecuteCommand2( pszCommand := refCommand, pszStdOut := refOutput, udiStdOutLen := SIZEOF(sOutput), pResult := ADR(resultCmd) ); Notes & Warnings This method gives full system access to the CODESYS runtime — do not expose this system to the public network without protection. Do not use sudo in commands inside CODESYS when the runtime is already running as root. @tomas111, in case you want to read the temperatur, use this command: sCommand:STRING:='/usr/bin/vcgencmd measure_temp';
Last updated: 2025-05-20

Post by artplc on 3.5 P20 hangs - no response CODESYS Forge talk (Post)
Subject: Solution: CODESYS Freezes Linked to Large Symbol Configuration (OPC UA) Hello everyone, I was experiencing the same problem as many here: CODESYS (v3.5 SP20+) freezing during compilation and download, especially on projects that use OPC UA for SCADA. The only workaround was to run Build > Clean all before every download. I believe I have found the root cause and a permanent solution. The Problem: The issue is directly related to a large Symbol Configuration. In my project, I only need about 700 tags for OPC UA, but the Symbol Configuration list was showing over 250,000 tags. This is because it automatically includes all variables from Global Variable Lists (GVLs) and other memory areas. When any static variable is changed, the IDE tries to rebuild this enormous list, causing it to freeze for several minutes. The Solution: Instead of letting CODESYS export every variable by default, you can use a pragma to tell it which variables or GVLs to exclude from the symbol export process. Place the following pragma at the top of any GVL or data structure that you do not want to be part of the symbol configuration: 1 {attribute 'symbol' := 'none'} 2 VAR_GLOBAL 3 // All variables in this GVL will now be excluded from the 4 // Symbol Configuration by default. 5 InternalVariable1 : INT; 6 InternalVariable2 : BOOL; 7 END_VAR After adding this pragma to all the GVLs that were not needed for OPC UA, the number of tags in my Symbol Configuration dropped to the correct amount. The compilation and download process is now fast again, with no freezing. This method is much more efficient than manually managing the symbol list in the editor. I hope this helps others who are facing this frustrating issue. Best regards.
Last updated: 2025-08-10

Post by e13740e on Сalling the context menu from the keyboard CODESYS Forge talk (Post)
Hello. I have a question for you, perhaps you have an answer to it. Regarding calling the Context menu from the keyboard in Devices, specifically calling the context menu window from the keyboard via an assigned combination. Goal: to have the ability to navigationally add internal Objects (Methods, Properties) to Function Blocks from the keyboard without using the mouse. The Codesys environment interface does not allow Windows access to this via Shift+F10. Externally (outside the Codesys environment), the hotkey call for the context menu (Shift+F10) from the keyboard works in various environments, but at the same time, in Codesys it does not (it doesn't work). I have already tried various options on my own to achieve this (but unsuccessfully): Tools\Customize\Keyboard\Objects (the set combination does not give the desired result) Tools\Customize\Keyboard\Objects... (the set combination is extremely useful as it provides access to adding objects directly to Devices but does not provide the ability to add an internal object (Method/Property) to a Function Block). Thank you in advance for your help!!! P.S. I have already started trying to implement a script-based solution to call the context menu via AutoHotkey - by relocating the mouse cursor to the area of the navigational selection with a subsequent call of the context menu on it and relocating the mouse cursor back to its initial position. And in order not to "reinvent the wheel," I am writing this request to you!!!
Last updated: 2025-10-05

Post by e13740e on Сalling the context menu from the keyboard CODESYS Forge talk (Post)
Hello. I have a question for you, perhaps you have an answer to it. Regarding calling the Context menu from the keyboard in Devices, specifically calling the context menu window from the keyboard via an assigned combination. Goal: to have the ability to navigationally add internal Objects (Methods, Properties) to Function Blocks from the keyboard without using the mouse. The Codesys environment interface does not allow Windows access to this via Shift+F10. Externally (outside the Codesys environment), the hotkey call for the context menu (Shift+F10) from the keyboard works in various environments, but at the same time, in Codesys it does not (it doesn't work). I have already tried various options on my own to achieve this (but unsuccessfully): Tools\Customize\Keyboard\Objects (the set combination does not give the desired result) Tools\Customize\Keyboard\Objects... (the set combination is extremely useful as it provides access to adding objects directly to Devices but does not provide the ability to add an internal object (Method/Property) to a Function Block). Thank you in advance for your help!!! P.S. I have already started trying to implement a script-based solution to call the context menu via AutoHotkey - by relocating the mouse cursor to the area of the navigational selection with a subsequent call of the context menu on it and relocating the mouse cursor back to its initial position. And in order not to "reinvent the wheel," I am writing this request to you!!!
Last updated: 2025-10-05

Post by john-robinson on Limiting Memory Access of an Array to Within its Bounds CODESYS Forge talk (Post)
Recently we had an issue regarding some simple code to calculate a rolling average. The code indexes from zero to 199 to properly store the current input into a circular buffer which then allows us to calculate a rolling average: VAR input_5s : REAL; outs_arr : ARRAY[0..199] OF REAL; i : USINT := 0; END_VAR ___ //this code runs every five seconds, calculating a rolling average outs_arr[i] := input_5s; i := i + 1; output := OSCAT_BASIC.ARRAY_AVG(ADR(outs_arr), SIZEOF(outs_arr)); IF i >= SIZEOF(outs_arr) THEN i := 0; END_IF There is a simple bug in this code where the index will be set to 0 when it has surpassed the length of the array in bytes (800 in this case) rather than larger than the number of reals in the array (200). The solution here is simple, replacing i >= SIZEOF(outs_arr) with i >= SIZEOF(outs_arr)/SIZEOF(outs_arr[0]). In this example when the index increased to 201 and the line outs_arr[201] := input_5s was called, codesys arbitrarily wrote to the address in memory that is where outs_arr[201] would be if the array was that long. I would like to find a way to wrap the codesys array inside of a wrapper class that checks if an input is within the bounds of an array before writing to that value. I know how I would implement that for a specific array, I could create a method or class that takes an input of an array of variable length, ie. ARRAY[*] OF REAL, but I don't know how to make this for any data type. I am wondering if anyone has ever done anything similar to this, or has any better suggestions to ensure that none of the programmers on this application accidentally create code that can arbitrarily write to other locations in memory.
Last updated: 2024-03-05

Post by xcqt on Oop best practice CODESYS Forge talk (Post)
Hi all, I’m currently trying to improve my OOP structure in CODESYS and I’m looking for some input on how others approach this. I understand the basics like inheritance, interfaces, abstract FBs, methods, and properties, but I still struggle a bit with the overall architecture and what’s considered clean or scalable in bigger projects. As an example, I’m working on two different energy meter function blocks: FB_EnergyMeter_MQTT reads data from MQTT (strings) FB_EnergyMeter_Modbus reads data from Modbus (words) Both have their own Update() method and implement the same interface (something like IF_EnergyMeter). Later on, I’ll probably add more meter types, but they should all behave the same from the controller’s point of view. Now, there’s a FB_GridControl block that needs power data from these meters. I see two options here: Define the meter blocks inside FB_GridControl and call them directly (for example fbModbusMeter.UpdateModbus()). Keep the meter blocks outside and pass them into FB_GridControl as interface references, so the control block doesn’t know which specific type of meter it’s dealing with. Option 2 feels cleaner and more flexible to me, but I’m not entirely sure how to handle the data flow in that case. Should I pass the meter instance through an interface reference (REFERENCE TO IF_EnergyMeter)? Or is there a better way to link the external FBs to the control block? I’d like to hear how you structure this kind of setup or see an example from someone who has done something similar. EDIT: I think i need to do something like this fbModbusUpdateInput(wInput:= wWordValue); fbMqttUpdateInput(strInput:= strStringValue); IF bUseMqtt THEN Meter REF = fbMqttUpdateInput; ELSE Meter REF = fbModbusUpdateInput; END_IF fbControl.SetMeter(UsedMeter := Meter); Or am i thinking wrong? Thanks, Thomas
Last updated: 2025-10-16

Post by mondinmr on Direct Pointers in IOMapping for EtherCAT with IoDrvEthercatLib.ETCSlave_Dia CODESYS Forge talk (Post)
I have found a very interesting solution using: IoConfigTaskMap IoConfigConnectorMap IoConfigChannelMap The first is the list of IO tasks. The second is the connector for each IO module in the IOMap. The third is the individual input or output on the IOMap. One of the properties of the connector is another pointer to a connector, which corresponds with the connector of the EtherCAT slave. Through this information, it is possible to understand to which EtherCAT slave an IO connectormap corresponds. I am attaching an FB that allows for the construction of an IO map and finding the pointer to the actual IOs in the IOMap based on the bitoffset. FUNCTION_BLOCK IOExplorer VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR inputChannels: COL.LinkedList; outputChannels: COL.LinkedList; ulintFactory: COL.UlintElementFactory; END_VAR METHOD inputAtBitOffsetOfConnector : POINTER TO BYTE VAR_INPUT conn: POINTER TO IoConfigConnectorMap; bitOffset: UDINT; END_VAR VAR it: COL.LinkedListIterator; itf: COL.IElement; elem: COL.iUlintElement; channelInfo: POINTER TO ADVChannelInfo; bitOffsetR: UDINT; END_VAR inputChannels.ElementIterator(it); WHILE it.HasNext() DO it.Next(itfElement => itf); __QUERYINTERFACE(itf, elem); {warning disable C0033} channelInfo := TO___UXINT(elem.UlintValue); {warning restire C0033} IF channelInfo^.connectorField = conn THEN IF bitOffsetR = bitOffset THEN inputAtBitOffsetOfConnector := channelInfo^.addr; RETURN; END_IF bitOffsetR := bitOffsetR + channelInfo^.size; ELSE bitOffsetR := 0; END_IF END_WHILE inputAtBitOffsetOfConnector := 0; END_METHOD METHOD outputAtBitOffsetOfConnector : POINTER TO BYTE VAR_INPUT conn: POINTER TO IoConfigConnectorMap; bitOffset: UDINT; END_VAR VAR it: COL.LinkedListIterator; itf: COL.IElement; elem: COL.iUlintElement; channelInfo: POINTER TO ADVChannelInfo; bitOffsetR: UDINT; END_VAR outputChannels.ElementIterator(it); WHILE it.HasNext() DO it.Next(itfElement => itf); __QUERYINTERFACE(itf, elem); {warning disable C0033} channelInfo := TO___UXINT(elem.UlintValue); {warning restire C0033} IF channelInfo^.connectorField = conn THEN IF bitOffsetR = bitOffset THEN outputAtBitOffsetOfConnector := channelInfo^.addr; RETURN; END_IF bitOffsetR := bitOffsetR + channelInfo^.size; ELSE bitOffsetR := 0; END_IF END_WHILE outputAtBitOffsetOfConnector := 0; END_METHOD METHOD scanIO VAR_INPUT END_VAR VAR numTasks: DINT := IoConfig_Globals.nIoConfigTaskMapCount; tType: WORD; ioTask: POINTER TO IoConfigTaskMap; numCon: WORD; connector: POINTER TO IoConfigConnectorMap; numCh: DWORD; channelInfo: POINTER TO ADVChannelInfo; iTsk: DINT; iCon: WORD; iCh: DWORD; i: DINT; _tmpConnList: COL.IList; elem: COL.IUlintElement; itf: COL.IElement; tmpCh: POINTER TO ADVChannelInfo; lastE: DINT; e: COL.COLLECTION_ERROR; e1: Error; END_VAR VAR_INST lF: COL.ListFactory; END_VAR IF outputChannels.CountElements() > 0 OR inputChannels.CountElements() > 0 THEN RETURN; END_IF _tmpConnList := lF.CreateDynamicList(16, 16); //Iterate through all IO tasks FOR iTsk := 0 TO numTasks - 1 DO ioTask := ADR(IoConfig_Globals.pIoConfigTaskMap[iTsk]); //Store the type of the task (Input or Output) tType := ioTask^.wType; numCon := ioTask^.wNumOfConnectorMap; //Iterate through all connectors of the task FOR iCon := 0 TO numCon - 1 DO connector := ADR(ioTask^.pConnectorMapList[iCon]); numCh := connector^.dwNumOfChannels; //Iterate through all channels of the connector FOR iCh := 0 TO numCh - 1 DO //Create a new channel info object and fill it with the address, connector and size of the channel //Connectors is address of field connector in this case like EtherCAT slave //Address is the address of the IOMap //Size is the size of channel data in bits in IOMap channelInfo := __NEW(ADVChannelInfo); channelInfo^.addr := connector^.pChannelMapList[iCh].pbyIecAddress; channelInfo^.connectorField := connector^.pConnector; channelInfo^.size := connector^.pChannelMapList[iCh].wSize; //We put the channel info a temporary ordered list //Order is based on the address of IOMap lastE := TO_DINT(_tmpConnList.CountElements()) - 1; FOR i := 0 TO lastE DO _tmpConnList.GetElementAt(udiPosition := TO_UDINT(i), itfElement => itf); __QUERYINTERFACE(itf, elem); {warning disable C0033} tmpCh := TO___UXINT(elem.UlintValue); {warning restire C0033} //If the address of the channel is smaller than the address of the channel in the list IF tmpCh^.addr > channelInfo^.addr THEN //Insert the channel in the list at the current position _tmpConnList.InsertElementAt(TO_UDINT(i), ulintFactory.Create(TO_ULINT(channelInfo))); //Clear the channel info pointer channelInfo := 0; //Exit the loop i := lastE + 1; END_IF END_FOR //If the channel info is not 0, it means that the channel was not inserted in the list IF channelInfo <> 0 THEN //Add the channel to the end of the list elem := ulintFactory.Create(TO_ULINT(channelInfo)); _tmpConnList.AddElement(elem); END_IF END_FOR //Iterate temporary list and add the channels to the input or output list lastE := TO_DINT(_tmpConnList.CountElements()) - 1; FOR i := 0 TO lastE DO _tmpConnList.GetElementAt(udiPosition := TO_UDINT(i), itfElement => itf); __QUERYINTERFACE(itf, elem); {warning disable C0033} tmpCh := TO___UXINT(elem.UlintValue); {warning restire C0033} //If type is input, add the channel to the input list IF tType = TaskMapTypes.TMT_INPUTS THEN e := inputChannels.AddElement(ulintFactory.Create(TO_ULINT(tmpCh))); //If type is output, add the channel to the output list ELSIF tType = TaskMapTypes.TMT_OUTPUTS THEN e := outputChannels.AddElement(ulintFactory.Create(TO_ULINT(tmpCh))); ELSE __DELETE(tmpCh); END_IF END_FOR //Clear the temporary list _tmpConnList.RemoveAllElements(); END_FOR END_FOR END_METHOD
Last updated: 2024-02-13

Post by gustavocsw on MQTT memory leak problem CODESYS Forge talk (Post)
Hello everyone, I'm using the IoT Library to implement the MQTT communication with my local broker server in order to publish and subscribe at specifics topics to share and consume information about my application. But, it seems that are occurring some memory leak problem in a "high" frequency (more than 10 Hz) subscribe process. I follow the same method as in IoT Lib exemples, and at first looks perfect but my PLC was rebooting frequently and when I check its memory usage that was increasing as fast as the subscribe massage was sent. I'm using a WEG PLC410 and a WEG PLC500, and this error occurred in both of them (including in CODESYS Control Win x64). The application sends to the system a message JSON with the float payload Ex. {"data" : 0.8500}, but this happens with a INT, or BOL as well. I use the follow code in my application to find the value: //FindFirstValueByKey VARs PROGRAM JSON_VELO VAR //------Setting the JSON Subscriber to Set the Relay Value jsonDataVelo : JSON.JSONData; jsonByteArrayReaderVelo : JSON.JSONByteArrayReader; xST1okVelo : BOOL; FindFirstValueByKeyVelo : JSON.FindFirstValueByKey; jsonElementVelo : JSON.JSONElement; xDoneReaderVelo : BOOL; xDoneFindVelo : BOOL; //STRING and WSTRING for Subscribe the massage sPayloadJsonVelo : STRING := 'opa'; psPayloadJsonVelo : POINTER TO BYTE := ADR(sPayloadJsonVelo); //wsPayloadJsonRelaySet : WSTRING := "opa"; wsPayloadJsonVelo : WSTRING := STRING_TO_WSTRING('opa'); pwsPayloadJsonVelo : POINTER TO WORD := ADR(wsPayloadJsonVelo); lrVelo : LREAL; xKeepAliveVelo : BOOL; xSetVelo : BOOL; RSSet : RS; LIMPAR : STRING; //Find the msg end sFindVelo : STRING := '}'; psFindVelo : POINTER TO STRING := ADR(sFindVelo); iLenVelo : INT; iSizeVelo : INT := 12; udiContMsg : UDINT; END_VAR // FindFirstValueByKey CODE // Relay Set configuration xSetVelo := MQTT_SUBSCRIBER.RSVelo.Q1; IF xSetVelo THEN xKeepAliveVelo := TRUE; END_IF IF xKeepAliveVelo THEN udiContMsg := udiContMsg + 1; iLenVelo := TO_INT(StrLenA(psPayloadJsonVelo)); iSizeVelo := iLenVelo - TO_INT(MQTT_SUBSCRIBER.udiPayloadSizeVelo); StrDeleteA(psPayloadJsonVelo,iSizeVelo,iLenVelo); wsPayloadJsonVelo := STRING_TO_WSTRING(sPayloadJsonVelo); pwsPayloadJsonVelo := ADR(wsPayloadJsonVelo); //MQTT.ConvertUTF8toUTF16(sourceStart:= ADR(sPayloadJsonVelo), targetStart:= ADR(wsPayloadJsonVelo), dwTargetBufferSize:= TAM, bStrictConversion:= 1); //Reset jsonByteArrayReader jsonByteArrayReaderVelo ( xExecute := TRUE, pwData := pwsPayloadJsonVelo, jsonData := jsonDataVelo, xDone => xDoneReaderVelo ); FindFirstValueByKeyVelo( xExecute := xDoneReaderVelo, wsKey := "data", diStartIndex:= 0, jsonData := jsonDataVelo, jsonElement => jsonElementVelo, xDone => xDoneFindVelo ); IF xDoneFindVelo THEN lrVelo := jsonElementVelo.value.lrValue; //Reset jsonByteArrayReader jsonByteArrayReaderVelo ( xExecute := FALSE, pwData := pwsPayloadJsonVelo, jsonData := jsonDataVelo, xDone => xDoneReaderVelo ); FindFirstValueByKeyVelo( xExecute := FALSE, wsKey := "data", diStartIndex:= 1, jsonData := jsonDataVelo, jsonElement => jsonElementVelo, xDone => xDoneFindVelo ); xKeepAliveVelo := FALSE; GVL.xSetVeloRead := TRUE; END_IF END_IF And this to subscribe at the topic: //SUBSCRIBE VAR: //----------------- Subscribe Velocity ----------------------- MQTTSubscribeVelo : MQTT.MQTTSubscribe;//Variable MQTTSubscriber block -X - function-X wsTopicSubscribeVelo : WSTRING(1024) := "CORE/odometry/GET/data/simp"; // Topic to publish a message sSubscribeMassageVelo : STRING; udiPayloadSizeVelo : UDINT; xSDoneVelo : BOOL; xSErrorVelo : BOOL; xReceiveVelo : BOOL; eSTypeVelo : MQTT.MQTT_ERROR; eSMQTTErrorVelo : MQTT.MQTT_ERROR; RSVelo : RS; udiCont : UDINT; //SUBSCRIBE CODE: MQTTSubscribeVelo( xEnable:= MQTT_CLIENT.xConnection_Broker AND NOT xSErrorVelo AND NOT JSON_VELO.xKeepAliveVelo, pbPayload:= JSON_VELO.psPayloadJsonVelo, udiMaxPayloadSize:= SIZEOF(JSON_VELO.sPayloadJsonVelo), udiPayloadSize => udiPayloadSizeVelo, mqttClient:= MQTT_CLIENT.ClientMQTT, wsTopicFilter:=wsTopicSubscribeVelo, xDone => xSDoneVelo, xError=> xSErrorVelo, xReceived => xReceiveVelo, eMQTTError=> eSMQTTErrorVelo ); RSVelo(SET := xReceiveVelo, RESET1 := JSON_VELO.xKeepAliveVelo);
Last updated: 2024-09-09

Post by patrik on Wish: CODESYSControl.cfg - again CODESYS Forge talk (Post)
Really good post. I hope that it gets seen by CoDeSys staff. I often find the documentation lacking when it comes to using CoDeSys in more complex ways. If using standard "basic PLC functionality", then it's fine and functions are documented in a sufficient way. Once you go outside of that bubble then I find the documentation not enough. CODESYSControl.cfg is a good example of this. And why isn't more of this integrated in the IDE. like changing port of the webserver. Why is it hidden in this file and not accessible through the IDE. It feels like a layer of complexity purposely left in so you don't change it if you don't know what you are doing. Like the argument is if you have the know-how to find the file you have the know-how to edit it. I find the documentation lacking when it comes to more complex libraries too. Like the element collections library. there is parameters I still don't quite know what they do. I can't find any documentation about them. There is an example project to download from CoDeSys so you can see how you are supposed to set your factories up. I leave some parameters like it is in the example. Should I? I don't know. Does it work. Yes. But I could perhaps create memory leaks if I get something wrong and I don't see that until way later. In the create method of your element factory you have a function called __vfinit and you just have to accept that it works. Why can't I get insight in what it does through documentation? Don't CoDeSys want us to know how these work so we can't use them to accidentally break something? I find the error logging lacking too. I've had the CoDeSys service crash but it doesn't say why. I've seen windows noticing the crash but no error code was sent or error message. Also in my current issue where the application fails to start after power outage. I can see in the log that it fails to load retain in the application as one log entry and application failing to start as another. But why? Why does loading the retain fail? give me more information! Is it just a skill issue with me? I don't know. If it is then I'd still want CoDeSys to step up their game when it comes to documentation. I'm sorry if a lot of frustration comes though in this post. I really like the product and what I can do with it compared to other PLC brands.
Last updated: 2024-11-21

Post by critcho on WebVisu Numpad dialog - Dialogtitle not accepting string variable CODESYS Forge talk (Post)
I have a WebVisu page which is re-used for multiple types of gas; it displays the alarm thresholds etc. for one gas at a time. I have an array of structures holding the set-points for each of the gasses. I'm trying to change the dialog title for the value input pop-up (VisuDialogs.Numpad). The Dialogtitle is set to Gas_Alarms.HighAlarmActivationLevelDialog, which is a string(50): When the gas type is changed (page first loaded, or prev/next buttons), METHOD Gas_Alarms.Update_Display_Variables is called, to update the strings used for the dialog titles so it shows GasName High Alarm Activation Level (EngineeringUnits), e.g. CO2 High Alarm Activation Level (PPM): HighAlarmActivationLevelDialog := Insert(' High Alarm Activation Level ()', EngineeringUnits, 30); HighAlarmActivationLevelDialog := Insert(HighAlarmActivationLevelDialog, GasName, 0); (That's not where the problem is, I'm just explaining why I'm using a variable dialogue title.) The variables used are definitely strings, but i'm always getting the error: "The type of the dialog title must be either STRING or WSTRING". I've tried moving HighAlarmActivationLevelDialog etc. to the GVL and also changing them to WSTRING, I even tried adding them to the structure array, but I always get the same error. Clearing the dialog title for that element removes the error - so I'm confident it's not a phantom error caused by something else. But I should be able to add these titles, can't just nuke them all to make it work. The MinVal and MaxVal for the Numpad are called from the array, e.g. GVL.Gas_Alarm_Configs[0].ScaledValueMIN, they are not getting an error. I am using array indexes 1 - 5 for the gasses, when I load the form or change gas, I copy the selected index to index 0 of the array, and values from Gas_Alarm_Configs[0].[ALL] are displayed on the page. When they hit save, I copy Gas_Alarm_Configs[0].[ALL] to Gas_Alarm_Configs[CurrentGasIndex].[ALL]. (ALL just means every element, I can't figure out how to display an asterix in this question... :) ) I've searched online for variable title dialog, but not found anything to help. Thanks for your help!
Last updated: 2025-05-29

Post by struccc on Inheritence of struct, CODESYS Forge talk (Post)
Strangely reminds me to my struggles... Want to do something "Elegant", reusable, universal, practical... In CODESYS??? 🙃 First of all, before you get too deep into this: If you could find a way, to make a "universal" log entry object, containing the variable length data itself, you wouldn't be able to store them in an array, or access them like an array, or pass them by value as a type. (please correct me, if I'm wrong, incorrect, or not precise). Because... Basically you can't declare a type with variable memory footprint. This is a very deeply embedded characteristic of CODESYS, and all IEC 61131-3 systems, and it has many reasons behind. And yes, it is a very common trap / mistake, to forget about. So, with a log entry - I guess - it's pretty much the purpose: store data and metadata together, and then handle it in a uniform way. There are ways to handle this, really depends on what is the purpose. For example: 1. Entries with fixed length (Maybe it is not as evil as it looks for the first time. Depends on the situation, but definitely the fastest and easiest code) You can have your base object, with an internal, fixed length string or byte array variable. I would go with a string, and call it _Data.; And then you can make properties, like As_Bool, As_Int, As_Real... In the 'set' accessors, you can do like: pReal := ADR(_Data); // POINTER TO REAL As_Real := pReal^; In the 'get' accessors, evidently: pReal := ADR(_Data); // POINTER TO REAL pReal^ := AS_Real; Or, can use ANY type, if you are not obsessed with variable / property like access: 2. Fixed length, but nicer First, some disadvantage to any values: - You can only assign values with write access. No literals, constants, etc... - Can only be used as input variable of function or function_block - Therefore, stg you could reach: LogEntry.Initialize (stVariable|rVariable|iVariable|xVariable); Just a quick example (it's funny to play with ANY): Be careful it was not tested. I'm sure can be done better, please feel free to comment FUNCTION_BLOCK FB_LogEntry VAR_INPUT MsgClass : UDINT; // Like DEBUG, WARN, ERR... MsgCode : UDINT; // Like Errors.ERR_FAILED MsgTS : DT; // The timestamp END_VAR VAR _Data : STRING(80); // Our data container... _Descr : __SYSTEM.AnyType; // A standard descriptor for our data, containing TYPE_CLASS, address and size END_VAR METHOD SET_Value : BOOL VAR_INPUT anyValue : ANY; END_VAR VAR I : DINT; diSize : DINT; pStr : POINTER TO STRING; END_VAR // Check what did we receive in anyValue. diSize := anyValue.diSize; // We use constant __SYSTEM.TYPE_CLASS to identify the received data type CASE anyValue.TypeClass OF // Maybe we don't want to store references, pointers... and who knows what else... __SYSTEM.TYPE_CLASS.TYPE_REFERENCE, __SYSTEM.TYPE_CLASS.TYPE_POINTER : SET_Value := FALSE; // For the planned types we will be just fine. TYPE_CLASS.TYPE_BOOL, TYPE_CLASS.TYPE_INT, TYPE_CLASS.TYPE_REAL : SET_Value := TRUE; // Optionally string can be handled separately, maybe we have received STRING(255), but practically it is shorter than 80 bytes... TYPE_CLASS.TYPE_STRING : pStr := anyValue.pValue; diSize := MIN(anyValue.diSize, LEN(pStr^) + 1); // Get the actual size, and rewrite the received structure member diSize := MIN(SIZEOF(_Data), diSize); // Can chop down the received string to our length... SET_Value := TRUE; // Maybe want to play a little bit more here, to narrow down or convert datatypes, etc... // Or just reject any other datatype ELSE SET_Value := FALSE; RETURN; END_CASE // Fail, if the received value is still larger than our container... IF diSize > SIZEOF(_Data) THEN SET_Value := FALSE; END_IF // Here we should be ok, just set up the _DataType structure, and copy store the data IF SET_Value THEN THIS^._Descr.TypeClass := anyValue.TypeClass; // The typeclass is already filtered THIS^._Descr.diSize := diSize; // Set the (adjusted) size THIS^._Descr.pValue := ADR(_Data); // This will not change, just to be sure {IF defined (pou:SysMem.SysMemCpy)} SysMem.SysMemCpy(_DataType.pValue, anyValue.pValue, TO_UDINT(anyValue.diSize)); {ELSE} // An ugly replacement MemCpy FOR I:=0 TO diSize - 1 DO _Descr.pValue[I] := anyValue.pValue[i]; END_FOR {END_IF} // Otherwise, in case of failure maybe better set an empty value (overwrite the former data descriptor) ELSE THIS^._Descr.TypeClass := TYPE_CLASS.TYPE_NONE; THIS^._Descr.pValue := ADR(_Data); THIS^._Descr.diSize := 0; END_IF METHOD GET_Value : BOOL VAR_INPUT anyValue : ANY; END_VAR VAR I : DINT; END_VAR // We just have to serve the data, using the __System.AnyType structure received // Roughly we can say: IF anyValue.TypeClass = _Descr.TypeClass AND anyValue.pValue <> 0 // This should not be possible, already taken care of by Codesys (?) THEN {IF defined (pou:SysMem.SysMemCpy)} SysMem.SysMemCpy(anyValue.pValue, _DataType.pValue, TO_UDINT(MIN(anyValue.diSize, _Descr.diSize))); {ELSE} // An ugly replacement MemCpy FOR I:=0 TO MIN(anyValue.diSize -1, _Descr.diSize - 1) DO anyValue.pValue[I] := _Descr.pValue[I]; END_FOR {END_IF} // Just to make sure, that our string is terminated... IF anyValue.TypeClass = TYPE_CLASS.TYPE_STRING THEN anyValue.pValue[anyValue.diSize -1] := 0; END_IF GET_Value := TRUE; RETURN; END_IF // ... But can play more CASE anyValue.TypeClass OF TYPE_CLASS.TYPE_WSTRING : ; // Could do conversion TYPE_CLASS.TYPE_XSTRING : ; // Wow, I have to figure this out TYPE_CLASS.TYPE_PARAMS : ; // BTW, what is this, how to use? TYPE_CLASS.TYPE_ANYNUM : ; // ... END_CASE Be careful it was not tested. I'm sure can be done better, please feel free to comment 3. If you really want to do entries with variable size In a standard environment, it would be similar to the previous, except you dont have the container variable _Data, just use a pointer, practically _Descr.pValue At Initialize (SET_Value), you have to allocate the memory, would be easy with SysMem.SysMemAlloc - nowadays with SysMem.SysMemAllocData -, and you make sure to release it after use with SysMem.SysMemFreeData... SysMemAlloc was already hidden. The problem with this, that sooner or later your application will totally fragment the dynamic memory, and fail... So should look for some form of dynMaybe MemUtils.MemoryManager (I am not sure what is the status and the future of it). 4. You will end up by a LogEntry Factory ... 5. You could still have a look at this IEC Snippets BTW, Standard Codesys Logger is not a bad choice either. If you are really interested, I share some more code / library.
Last updated: 2025-03-09

Post by hwillems on Ranges, Lambdas, on Fixed arrays of structs CODESYS Forge talk (Post)
I do datastructures and algorithms in Codesys. For example a Struct of Person with thing's like IdNumber, Name, Age etc. as example. Now i do all kind of calculations, filters. So i have this pretty big Fixed Array with Structs. On this struct i want to do simple stuff you can do easily in C++/Python/Rust etc. For example i want to do this: AvererageAge := Average(Peoples.Age); Then it will return the average of all members ages. Or Sort struct on age etc. Or sort on alphabetical Name. Or use Lambda functions to filter/mutate out things like, filter out everybody above 18 years old. Or remove people who it's name start with "A". Currently i have to write my own custom function for example sorting on Age. And make a super specific function based on that particulare datastructure. Here an Example: (*Before calling this FIlter method, set the mNodeFilterSwitch to the desired filter.*) CASE mNodeFilterSelect OF (********************************[ Status Filters ]***********************************) NodeID: FOR x := ACS_OUT_BEGIN TO ACS_OUT_END BY 1 DO FOR y := ACS_IN_BEGIN TO ACS_IN_END BY 1 DO IF marrNode[y].Status.oiNodeID > marrNode[y + 1].Status.oiNodeID THEN mNodeTemp := marrNode[y + 1]; marrNode[y + 1] := marrNode[y]; marrNode[y] := mNodeTemp; END_IF; END_FOR; END_FOR; Started: FOR x := DES_OUT_BEGIN TO DES_OUT_END BY -1 DO FOR y := DES_IN_BEGIN TO DES_IN_END BY -1 DO IF marrNode[y].Status.oxStarted > marrNode[y - 1].Status.oxStarted THEN mNodeTemp := marrNode[y - 1]; marrNode[y - 1] := marrNode[y]; marrNode[y] := mNodeTemp; END_IF; END_FOR; END_FOR; Starting: FOR x := DES_OUT_BEGIN TO DES_OUT_END BY -1 DO FOR y := DES_IN_BEGIN TO DES_IN_END BY -1 DO IF marrNode[y].Status.oxStarting > marrNode[y - 1].Status.oxStarting THEN mNodeTemp := marrNode[y - 1]; marrNode[y - 1] := marrNode[y]; marrNode[y] := mNodeTemp; END_IF; END_FOR; END_FOR; END_CASE; I have like 30+ of these in the enum. Not really DRY code right? These are custom made bubble sort filters in a function. You pass in the Datastructure, and say what function you want. (This is an enum collection of sorting functions) And then the Array with Nodes of Structs gets ordered. Why can't we have Iterators and Lambda's and build in standard functions like regular languages? Also i use bubble sort because it's the easiest to implement because i can't get this to code DRY. Problem with ST (Even the new one with classes) that it's very limited for programming datastructures and algorithms. Yes you still not want dynamic memory and you need to choose the correct algorithm so you know the most extreme edge cases regarding the time it takes to execute the algorithms.(Real-time execution) How are other people dealing with this? Here for example saw some software using an adjusted ST language and having FOR EACH possibility: https://www.fernhillsoftware.com/help/iec-61131/structured-text/st-for-each.html You can then build your own custom Iterator functions. I wish the IEC 61131-3 standard would be more expressive and having more standard modern features, but still keep close to the fact of no dynamics memory and real-time systems.
Last updated: 2023-08-31

Post by scoob on ModbusFB - Slow Response Time CODESYS Forge talk (Post)
Hello, I have been trying to use the ModbusFB functions so I can put some code into libraries, but it seems to be very slow for me. I have a Modbus device with 100ms registers. I previously setup 10 channels in the 'traditional' Modbus Slave with channels and mappings - and set a cyclic trigger at 100ms - this worked fine. I then tried the ModbusFB example, and setup reading the same 10 blocks of modbus addresses, copying the example and putting all of the requests into an array and triggering the requests sequentially. I timed how long the requests are taking to get round to each one, and it is around 1s 450ms. How do I speed this up to match the cyclic time? IF NOT(init) THEN init := TRUE; // Set the required IP address: ipAddress[0] := 192; ipAddress[1] := 168; ipAddress[2] := 1; ipAddress[3] := 10; // Pass the required IP address to the clinet FB: client_NetworkSwitch.aIPaddr := ipAddress; client_NetworkSwitch.udiLogOptions := (ModbusFB.LoggingOptions.ClientConnectDisconnect OR ModbusFB.LoggingOptions.ClientReceivedValidReplies); // Try to connect the client client_NetworkSwitch(xConnect:=TRUE); // Configure all the channels to read connecting them to the client: portStatus_Request(rClient := client_NetworkSwitch, uiStartItem := 4096, uiQuantity := 32, pData := ADR(portStatus), udiReplyTimeout := udiReplyTimeout); portSpeed_Request(rClient := client_NetworkSwitch, uiStartItem := 4352, uiQuantity := 32, pData := ADR(portSpeed)); flowControl_Request(rClient := client_NetworkSwitch, uiStartItem := 4608, uiQuantity := 32, pData := ADR(flowControl)); linkUpCounter_Request(rClient := client_NetworkSwitch, uiStartItem := 5888, uiQuantity := 32, pData := ADR(linkUpCounter)); txPacketCounter1_Request(rClient := client_NetworkSwitch, uiStartItem := 8192, uiQuantity := 100, pData := ADR(txPacketCounter1)); txPacketCounter2_Request(rClient := client_NetworkSwitch, uiStartItem := 8292, uiQuantity := 28, pData := ADR(txPacketCounter2)); rxPacketCounter1_Request(rClient := client_NetworkSwitch, uiStartItem := 8448, uiQuantity := 100, pData := ADR(rxPacketCounter1)); rxPacketCounter2_Request(rClient := client_NetworkSwitch, uiStartItem := 8548, uiQuantity := 28, pData := ADR(rxPacketCounter2)); txErrors_Request(rClient := client_NetworkSwitch, uiStartItem := 8704, uiQuantity := 64, pData := ADR(txErrors)); rxErrors_Request(rClient := client_NetworkSwitch, uiStartItem := 8960, uiQuantity := 64, pData := ADR(rxErrors)); // Trigger all client requests initially FOR clientRequestsCnt := 0 TO (SIZEOF(clientRequests)/SIZEOF(clientRequests[0]))-1 DO pClientRequest := clientRequests[clientRequestsCnt]; pClientRequest^.xExecute := TRUE; END_FOR // Prepare sequential trigger / control of client requests. clientRequestsCnt := 0; pClientRequest := clientRequests[clientRequestsCnt]; END_IF // Call the client to do request processing: client_NetworkSwitch(); // Now we trigger client request sequentially ... IF NOT pClientRequest^.xExecute AND NOT pClientRequest^.xDone AND run AND client_NetworkSwitch.xConnected THEN pClientRequest^.xExecute := TRUE; END_IF // .. and check result/error IF pClientRequest^.xExecute AND run AND client_NetworkSwitch.xConnected THEN IF pClientRequest^.xDone THEN // Prepare next trigger of client request (a rising edge of xExecute) pClientRequest^.xExecute := FALSE; IF clientRequestsCnt < SIZEOF(clientRequests)/SIZEOF(clientRequests[0])-1 THEN // next client request clientRequestsCnt := clientRequestsCnt + 1; ELSE clientRequestsIterationCounter := clientRequestsIterationCounter + 1; clientRequestsCnt := 0; END_IF pClientRequest := clientRequests[clientRequestsCnt]; END_IF END_IF I did try a semi-coded way using the IoDrvModbusTCP library, and setting the slave com settings, then 10 commands and 10 requests, then using a TP on xDone as a pause, before triggering another request - this is time the delay is around 120ms - so the device is fine with the speed, just something I am doing wrong in the ModbusFB method I am sure.
Last updated: 2024-04-26

Post by ihatemaryfisher on Sorting array of any-sized structure CODESYS Forge talk (Post)
In my machine's operation, I need to display multiples tables containing arrays of structured variables. The arrays change during operation, and my supervisor has advised me to write a new bubble-sort for each array. I think I can make a function to sort an array of any data type. This was my own project, and I'm a relatively new coder. I want to know the weaknesses in my approach, and a better method, if one exists. As far as I can test, the function accepts an array of a structured variable of any size, and sort it by any VAR in that structure. But it relies heavily on pointers, which I've heard are bad practice? Function call: // SORT BY BYTE-SIZED VAR IF xDoIt[6] THEN FUNBubbleSortSansBuffer( IN_pbySourcePointer := ADR(astArray[1]), // address of first byte in first element of array IN_pbyComparePointer:= ADR(astArray[1].byCompByte), // points to first byte of the comparing variable (variable you sort by) IN_uiStructureSize := SIZEOF(TYPE_STRUCTURE), // size, in bytes, of the structured variable IN_uiCompareSize := SIZEOF(astArray[1].byCompByte), // size, in bytes, of the comparing variable (variable you sort by) diArrayElements := UPPER_BOUND(astArray,1), // number of elements in array IN_xSmallToLarge := xSortOrder // whether to sort by small2large or large2small ); END_IF Function: FUNCTION FUNBubbleSortSansBuffer : BOOL VAR_INPUT IN_pbySourcePointer : POINTER TO BYTE; // points to beginning of array (first byte of first element) IN_pbyComparePointer: POINTER TO BYTE; // points to first byte of the comparing variable (variable you sort by) IN_uiStructureSize : UINT; // size, in bytes, of the structured variable IN_uiCompareSize : UINT; // size, in bytes, of the comparing variable (variable you sort by) diArrayElements : DINT; // number of elements in array IN_xSmallToLarge : BOOL; // whether to sort by small2large or large2small END_VAR VAR j : DINT; // repeat iteration over array until array ends i : DINT; // iterarte over array, swapping when necesary k : DINT; // iterator from 1 to size of structure (stepping 'through' a single element in array) dwSize : DWORD; // internal var for use in MEMUtils.MemCpy(<size>) // FOR SORTING BY BYTE VAR pbySourcePointer : POINTER TO BYTE; pbySourcePointer2 : POINTER TO BYTE; pbyComparePointer : POINTER TO BYTE; pbyComparePointer2 : POINTER TO BYTE; pbyPointerToBuffer : POINTER TO BYTE; // pointer to single byte buffer byBufferByte : BYTE; // single byte buffer END_VAR dwSize := UINT_TO_DWORD(IN_uiStructureSize); // get structure size (number of bytes) pbyPointerToBuffer := ADR(byBufferByte); // assign pointer to address of buffer byte (because MEMUtils.MemCpy requires a pointer input) CASE IN_uiCompareSize OF // depending on the size of the VAR to sort by (current functionality for BYTE and WORD/INT 1: // BYTE (8 BIT) FOR j := 1 TO diArrayElements DO // for number of elements in array FOR i := 1 TO (diArrayElements-1) DO // same thing, but row[i+1] row is included in swap logic pbySourcePointer := IN_pbySourcePointer + dwSize*(i-1); // point at #1 byte in array element[i] pbySourcePointer2 := pbySourcePointer + dwSize; // point at #1 byte in array element[i+1] // NOTE: because of memory locations, each array element is offset from one another by a number of bytes equal to the size of the structure // We can "walk" from array[i] to array[i+1] via steps equal to the size of the structure // e.g., ADR(array[i+1]) == ADR(array[i]) + SIZEOF([array datatype]) pbyComparePointer := IN_pbyComparePointer + dwSize*(i-1); // point to sorting variable in array element[i] pbyComparePointer2 := pbyComparePointer + dwSize; // point to sorting variable in array element[i+1] // using sort order (small -> large/large -> small) IF SEL(IN_xSmallToLarge, (pbyComparePointer2^ > pbyComparePointer^),(pbyComparePointer2^ < pbyComparePointer^)) THEN // This is where it gets tricky. We've identified pointers for the starting bytes of aArray[i] and aArray[i+1] // and we know the size of aArray[i]. We are going to swap individual bytes, one at a time, from aArray[i] and aArray[i+1] // this allows us to use only a single byte var as a buffer or temporary data storage // e.g., consider a structure consisting of a word, a byte, and a string. it is stored like this // |------WORD-------| |--BYTE-| |STRING------...| // astArray[1] == 1000 0100 0010 0001 1100 0011 1010 1010.... etc // astArray[2] == 0001 0010 0100 1000 0011 1100 0101 0101.... etc // performing a single swap (copy into a buffer, etc.) of the first byte of each array element creates this // astArray[1] == 0001 0100 0010 0001 1100 0011 1010 1010.... etc // astArray[2] == 1000 0010 0100 1000 0011 1100 0101 0101.... etc // incrementing the pointer adresses for the swap by 1 and swapping again swaps the next byte in each array element // astArray[1] == 0001 0010 0010 0001 1100 0011 1010 1010.... etc // astArray[2] == 1000 0100 0100 1000 0011 1100 0101 0101.... etc // continuing this from k to SIZEOF(TYPE_STRUCTURE) results in a toally swapped row FOR k := 1 TO IN_uiStructureSize DO // copy single byte[k] of array element 1 to buffer MEMUtils.MemCpy(pbyDest := (pbyPointerToBuffer), pbySrc := (pbySourcePointer+k-1), dwSize := 1); // copy single byte[k] of array element 2 to 1 MEMUtils.MemCpy(pbyDest := pbySourcePointer+k-1, pbySrc := (pbySourcePointer2+k-1), dwSize := 1); // copy buffer to byte[k] array element 2 MEMUtils.MemCpy(pbyDest := (pbySourcePointer2+k-1), pbySrc := pbyPointerToBuffer, dwSize := 1); END_FOR END_IF END_FOR END_FOR
Last updated: 2023-08-17

Post by kamalsingh on Temu Coupon $100 off [acr552049] or [acr552049] For New And Existing Users CODESYS Forge talk (Post)
To get $100 off, sign up as a new user using referral code [acr552049] or [acr552049] and enter the coupon during checkout when making your first purchase at Temu. You will receive the benefit once the coupon applied. Looking to save some extra cash on your favorite items? Look no further than Temu promo and coupon codes! Take advantage of these great deals to get the items you love at a discounted price. Don't miss out on these savings opportunities. Get a $100 discount on your Temu order with the promo code '[acr552049] or [acr552049]'. You can get a discount by clicking on the item to purchase and entering the code. Redeem $100 Off Temu Coupon Bundle Code [[acr552049] on TEMU App is a shopping platform that provides us with the best-branded items at discounted prices. You will also notice that TEMU offers users to save extra by applying the TEMU coupon code during checkout. You can get $100 off Temu by using the coupon code '[acr552049] or '. Existing customers can use this code. Temu existing user coupon code: [[acr552049] or new Using Temu's coupon code [[acr552049] or [acr552049]] will get you $100 off, access to exclusive deals, and benefits for additional savings. Save 40% off with Temu coupon codes. New and existing customer offers. What is Temu $100 Coupon Bundle? New Temu $100 coupon bundle includes $120 worth of Temu coupon codes. The Temu $100 Coupon code [[acr552049] or [acr552049]] can be used by new and existing Temu users to get a discount on their purchases. Temu $100 Coupon Bundle Code [[acr552049] or [acr552049]] Temu Coupon Code France: {acr552049} Temu Coupon Code Sweden: {acr552049} Temu Coupon Code Australia: {acr552049} Temu Coupon Code United Kingdom: {acr552049} Temu Coupon Code Spain: {acr552049 }or {acr552049} Temu Coupon Code Italy: {acr552049} Temu Coupon Code Germany: {acr552049} Temu Coupon Code Saudi Arabia: {acr552049} Temu Coupon Code Austria: {acr552049} Temu Coupon Code Belgium: {acr552049} Temu Coupon Code Thailand: {acr552049} Temu Coupon Code Kuwait: {acr552049} Temu Coupon Code United Arab Emirates: { acr552049} Temu Coupon Code Switzerland: {acr552049} Temu Coupon Code Mexico: {acr552049} Temu Coupon Code New Zealand: {acr552049} Temu Coupon Code Poland : {acr552049} Temu Coupon Code United States: {acr552049} Temu Coupon Code Portugal: {acr552049} Temu Coupon Code Netherlands: {acr552049} Temu Coupon Code Brazil: {acr552049} Temu Coupon Code Norway: {acr552049} Temu coupon $100 off for existing customers There are a number of discounts and deals shoppers can take advantage of with the Teemu Coupon Bundle [[acr552049] or [acr552049]]. Temu coupon $100 off for existing customers'[acr552049] or [acr552049]' will save you $100 on your order. To get a discount, click on the item to purchase and enter the code. You can think of it as a supercharged savings pack for all your shopping needs Temu coupon code 80% off – [[acr552049] or [acr552049]] Free Temu codes 50% off – [[acr552049] or [acr552049]] Temu coupon $100 off – [[acr552049] or [acr552049]] Temu buy to get $39 – [[acr552049] or [acr552049]] Temu 129 coupon bundle – [[acr552049] or [acr552049]] Temu buy 3 to get $99 – [[acr552049] ] or [acr552049]] Exclusive $100 Off Temu Coupon Code Temu $100 Off Coupon Code: ([acr552049] or [acr552049]) Temu Coupon Code $100 Bundle[acr552049] or [acr552049]) Free Gift On Temu: ([acr552049] or [acr552049]) Temu $100 off coupon code for Exsting users: ([acr552049] or [acr552049]) Temu coupon code $100 off free shipping You will save $100 when you use Temu's 90% OFF promo code [[acr552049] or [acr552049]]. Enter the discount code when purchasing an item How Does Temu $100 Coupon Work Temu Coupon Code Colombia: {acr552049}or {acr552049} Temu Coupon Code Chile: {acr552049}or {acr552049} Temu Coupon Code Israel: {acr552049}or {acr552049} Temu Coupon Code Egypt: {acr552049}or {acr552049 } Temu Coupon Code Peru {acr552049}or {acr552049} Temu Coupon Code Ireland: {acr552049}or {acr552049} Temu Coupon Code Hungary: {acr552049}or {acr552049} Temu Coupon Romania code: {acr552049 }or {acr552049} Temu's $100 promo code isn't just one big coupon you use all at once. Instead, think of it as a welcome package filled with different discounts and offers worth $100. New customers are welcome. Temu coupon code $100 off Temu 90% OFF promo code '[acr552049] or [acr552049]' will save you $100 on your order. To get a discount, click on the item to purchase and enter the code. Yes, Temu offers $100 off coupon code “[acr552049] or [acr552049]” for first time users. You can get a $100 bonus plus 30% off any purchase at Temu with the $100 Coupon Bundle at Temu if you sign up with the referral code [[acr552049] or [acr552049]] and make a first purchase of $100 or more. How Do Apply Temu Coupon Code [[acr552049] or [acr552049]]? 1.Download the TEMU app and create a new account. 2.Fill in basic details to complete account verification. 3.Select the item you want to purchase and add it to your cart Minimum of $100. 4.Click on the Coupon Code option and enter the TEMU Coupon Code. 5.Once the coupon has been applied, you will see the final discount. 6.price Select your payment method and complete your purchase. Temu coupon code $100 off first time user yes, If you're a first-time user, Temu offers $100 off with coupon code '[acr552049] or [acr552049]' Temu offers first-time users a $100 discount. Here are some Temu coupons! The fact that new users can benefit from such generous discounts is great. How do you redeem Temu $100 coupon code? Yes, To redeem the Temu $100 coupon code, follow these steps: 1.Sign Up: If you haven't already, sign up for a Temu account on their website or app. 2.Add Items to Cart: Browse through the products you'd like to purchase. Add items worth $100 or more to your cart. 3.Apply Coupon Code: During checkout, enter the coupon code “[acr552049] or [acr552049]” in the designated field. This will unlock the $100 coupon bundle. You can also use the referral code “[acr552049] or [acr552049]” when signing up to receive the same benefit. 4.Enjoy Savings: The discount will be applied, and you'll enjoy additional savings on your purchase. Plus, you can combine this with other available discounts, such as the 30% off code for fashion, home, and beauty categories. Temu coupon code $100 off Yes, You can use the coupon code [[acr552049] or [acr552049]] to get the $100 coupon bundle. On your next purchase, you will also receive a 50% discount. If you use Temu for your shipping, you can save some money by taking advantage of this offer. Temu Coupon $100 off For New And Existing Users New users at Temu receive a $100 discount on orders over $200 Use the code [[acr552049] or [acr552049]] during checkout to get Temu Coupon $100 off For New And Existing Users. You can save $100 off your first order with the coupon code available for a limited time only. Extra 30% off for new or existing customers. Up to 40% off, $100 discount & more. what are temu codes - [acr552049] or [acr552049] does temu give you $100- [[acr552049] or [acr552049]] Conclusion The $100 Temu Coupon Bundle is an excellent opportunity to unlock substantial discounts on a wide range of products and services if you're a savvy shopper. Temu 90% OFF promo code [[acr552049] or [acr552049]] will save you $100 on your order. To get a discount, click on the item to purchase and enter the code FAQs How to use a $100 dollar coupon on Temu Yes, you can use Temu's $100 coupon. There is a code for Temu that saves money on purchases called '[[acr552049] or [acr552049]]'. Where can I get a temu coupon code $100 off? Yes, by using the special legit Temu coupon code '[acr552049] or [acr552049]' during checkout after downloading the app, you can get a $100 Temu coupon code. You can also enjoy discounts of up to 90% off on selected items along with the $100 bundle coupon. Is there any $100 Temu coupon code available? TEMU offers high-quality products at low prices, with free shipping. You can use the coupon code “[[acr552049] or [acr552049]]” to get $100 off your first purchase of $100 or more and enjoy free shipping when you sign up for TEMU! Temu coupon code $100 off free shipping TEMU offers high-quality products at low prices, with free shipping. You can use the coupon code “[[acr552049] or [acr552049]]” to get $100 off your first purchase of $100 or more and enjoy free shipping when you sign up for TEMU! Get a $100 off coupon for Temu, you can use the coupon code [[acr552049] or [acr552049]] at checkout. This code is specifically for existing customers. How can I redeem the Temu $100 coupon? Type in the special coupon code [[acr552049] or [acr552049]] where it asks for it .Press the “Redeem” or “Apply” button to make your $100 coupon bundle active How To use your Temu $100 coupon [[acr552049] or [acr552049]] at checkout: Use our Temu promo code [[acr552049] or [acr552049]] during checkout and enjoy up to 90% off and a $100 welcome bonus. GRAB your discount and keep shopping
Last updated: 2024-10-26

<< < 1 .. 5 6 7 8 > >> (Page 7 of 8)

Showing results of 176

Sort by relevance or date