Post by timvh on VisuElems.CurrentUserGroupId is not stable
CODESYS Forge
talk
(Post)
I'm not sure what you are trying to do, but getting the CurrentUserGroupID like this will not work, because there could be multiple Visualization Clients and each can have a different user that is logged in. Also when you go online with CODESYS and open an Visualization, this is counted as a client. Probably this is the reason you see it changing. What you can to is "iterate" over all clients and then see which user is logged in on which visualization Client. For this you need to add the Visu Utils library to the project and call the FbIterateClients. See https://content.helpme-codesys.com/en/libs/Visu%20Utils/4.4.0.0/VisuUtils/VisuActionUtilities/Function-Blocks/FbIterateClients.html fbClientIteration( xExecute := x_Execute, itfClientFilter := VU.Globals.AllClients, itfIterationCallback := fbIterator, xDone => x_Done, xBusy => x_Busy, xError => x_Error, eError => e_Error); The fbIterator, in the example above, should be an instance of an FB which you have created yourself and this must implement VU.IVisualizationClientIteration. For example: FUNCTION_BLOCK FB_ITERATOR IMPLEMENTS VU.IVisualizationClientIteration Then automatically the corresponding methods will be called. In the HandleClient Method, you will get an interface to the client(s) and then you can get the current user through this interface: itfClient.UserGroupId You can also get the UserName: itfClient.UserName
Last updated: 2023-11-14
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 alexgooi on Modbus writing on value change
CODESYS Forge
talk
(Post)
Hi Duvan, You could make this in 1 single object (FB), Indeed don't use a function for this beacuse you need some memory to keep the old value. For i := 0 TO 200 BY 1 DO //Check if the value has been changed IF Old_Value[i] <> Value[i] THEN //Set the trigger to TRUE Trigger[i] := TRUE; Old_Value[i] := Value[i]; END_IF END_FOR If you define the Value array as an In_Out and the Trigger as an In_Out you arn't claiming any aditional memory to your system. You ofcourse then need to add some code arround it that does something with the trigger and writes it back to FALSE again. If you want more flexability you also could use pointers instead of using the IN_OUT FOR i := 0 TO 200 BY 1 DO address := address_Input + i * SIZEOF(*Put type here); IF Address^ <> Old_Value[i] THEN Trigger[i] := TRUE; Old_Value[i] := Address^; END_IF END_FOR
Last updated: 2024-04-02
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 paro on Modbus Client Request Not Processed
CODESYS Forge
talk
(Post)
Hi, Problem in your code is that you are not calling the ClientFb cyclic. Please look at the Example from CODESYS: https://forge.codesys.com/prj/codesys-example/modbus/home/Home/ Your code works if the client fb is called cyclic! IF NOT initDone THEN initDone := TRUE; // configure clientTcp clientTcp(aIPaddr:=Ethernet.IPAddress, uiPort:=502); // configure clientSerial clientSerial(iPort:=SysCom.SYS_COMPORT2, dwBaudRate:=SysCom.SYS_BR_115200, byDataBits:=8, eParity:=SysCom.SYS_EVENPARITY, eStopBits:=SysCom.SYS_ONESTOPBIT, eRtuAscii:=ModbusFB.RtuAscii.RTU); // configure clientRequestReadCoils_1 clientRequestReadCoils_1(rClient:=clientTcp, uiUnitId:=42, udiTimeout:=TO_UDINT(T#10MS)); // configure clientRequestWriteSingleRegister clientRequestWriteSingleRegister(rClient:=clientTcp, uiUnitId:=43, udiTimeout:=TO_UDINT(T#10MS)); // configure clientRequestReadCoils_2 clientRequestReadCoils_2(rClient:=clientTcp, uiUnitId:=44, udiTimeout:=TO_UDINT(T#10MS)); // configure clientRequestReadInputRegisters clientRequestReadInputRegisters(rClient:=clientTcp, uiUnitId:=44, udiTimeout:=TO_UDINT(T#10MS)); END_IF // call the client FB's clientTcp(); clientSerial(); // call client request FB's clientRequestReadCoils_1(rClient:=clientTcp, xExecute:=TRUE, uiStartItem:=2, uiQuantity:=3, pData:=ADR(aDataCoils_1[0])); // for more details see Example_TCP, especially ClientRequest control (xExecute, xDone, xError). IF clientRequestReadCoils_1.xDone THEN // get data from aDataCoils_1 ... END_IF clientRequestWriteSingleRegister(rClient:=clientTcp, xExecute:=TRUE, uiItem:=3, uiValue:=123); clientRequestReadCoils_2(rClient:=clientTcp, xExecute:=TRUE, uiStartItem:=2, uiQuantity:=3, pData:=ADR(aDataCoils_2[0])); clientRequestReadInputRegisters(rClient:=clientTcp, xExecute:=TRUE, uiStartItem:=16, uiQuantity:=3, pData:=ADR(aDataInputRegisters[0]));
Last updated: 2024-05-29
Post by timvh on How to implement an interface (IElement)?
CODESYS Forge
talk
(Post)
See: https://forge.codesys.com/prj/codesys-example/element-collect/home/Home/ This contains an application "OnlineChangeSafeLinkedListExample". What you should do is create a new interface which has your "Priority" property. Then your FB should extend the base element function block and implement your own interface: E.g. FUNCTION_BLOCK MyElement EXTENDS COL.LinkedListElementBase IMPLEMENTS I_MyInterface Then the __QUERYINTERFACE does the magic to check if your "element" also implements your interface. Something like this: // Compares this element with itfElement. // Returns 0 if the elements are equal, < 0 if the element is less than itfElement, // > 0 if the element is greater than itfElement. // This method will be called from sorted collections (e.g. |COL.SortedList|) to sort the elements. // IMPORTANT: The underlying value to be compared with MUST NOT be changed during the lifecycle of the object. METHOD ElementCompareTo : INT VAR_INPUT (* The element to compare*) itfElement : COL.IElement; END_VAR VAR itfIntElement : I_MyInterface; xResult : BOOL; END_VAR // We use integer iInt1 for sorting. xResult := __QUERYINTERFACE(itfElement, itfIntElement); IF xResult THEN IF iInt1 < itfIntElement.Priority THEN ElementCompareTo := -1; ELSIF iInt1 > itfIntElement.Priority THEN ElementCompareTo := 1; ELSE ElementCompareTo := 0; END_IF ELSE ElementCompareTo := -1; END_IF
Last updated: 2024-07-22
Post by mecmag on gear over gear (2 masters,1 slave) - how to do it?
CODESYS Forge
talk
(Post)
Hello, i need to create an application that uses 2 masters, with 2 different ratios, and it outputs in one single slave. I explain better: Imagine axis A, B and C. -Axis A is the slave. -Axis B is master of A, with a specific ratio. -Axis C is master of A, with a specific ratio. the result motion from B and C should be executed by A, proportionally in position and ratios from B and C. With standard softmotion libraries (MC_...) it is not possible (i couldn't) because it is allowed only one master with its ratio. I have made a similiar application in past, using MoveOverGear in order to synch gears before engage. Instead this time, i need to do a gear over gear application, using 2 different masters. The position have to be the sum of both masters (considering their ratios). Any clue about how to do it? Or were to find some FB that can do it?
Last updated: 2024-08-16
Post by manuknecht on Opening a Dialog on a specific Client from ST
CODESYS Forge
talk
(Post)
I managed to find a solution that seems to work reliably. As the VU.Globals.CurrentClient-filter accesses the CURRENTCLIENTID or at least a similar, internal variable it can only be used if called from a certain client (e.g. from a button in a visualization). My solution works by implementing a new client filter that compares the client ID of all clients to the ID of the last client that was used. The variable containing the data of the last client is defined as: G_LastClient : VU.IVisualizationClient; // Copy of last client that detected click This last client is then updated every time a button is pressed using the Execute ST-Code input configuration of the button: G_LastClient := VU.PublicVariables.Clients.Current; Next, I created a function block that implements the client filter interface as so: FUNCTION_BLOCK FB_LastClientFilter IMPLEMENTS VU.IVisualizationClientFilter VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR END_VAR Then i added a method to the FB called IsAccepted which is used to filter out the client. When creating the method, it should automatically be filled with the according variable declaration, as it is defined in the interface: (* For every client can be desided, if it is accepted. ``TRUE``: Client is accepted*) METHOD IsAccepted : BOOL VAR_INPUT (* The client, to check*) itfClient : VU.IVisualizationClient; END_VAR Now the client can be compared to the last used client as such: // check if clientID corresponds to clientID of last recorderd client IF itfCLient.ClientId = G_LastClient.ClientId THEN IsAccepted := TRUE; ELSE IsAccepted := FALSE; END_IF To make use of this custom client filter, initialize a variable with the client filter: LastClient : FB_LastClientFilter; // Client filter to find last used client Then use this client filter when opening or closing a dialog from ST: fbOpenMyDialog(itfClientFilter:=LastClient,xExecute:=TRUE,sDialogName:='VIS_MyDialog_DLG');
Last updated: 2023-09-27
Post by mubeta on Some 'pathetic' errors in SoftMotion program
CODESYS Forge
talk
(Post)
Thank you for your interest. Your answers are in line with what I knew, so at least it comforts me that I did not misinterpret the situation. However, I don't have an exact match as, for this project over the past few days I have: 1) I have gone back to leaving the various FBs of the motion always called, all of them, and in the state machine I use a boolean to activate the various useful Execute. (But in the future I want to go back and try the programming technique with which I wanted to develop this project); 2) For the occasional error: SMC_FB_WASNT_CALLED_DURING_MOTION perhaps it was due to the fact that I had set the Ethercat bus synchronism only at the CAN master level, but not at the level of individual drives. I have now also activated it for the individual drives and it does indeed seem to have been resolved, but having also adopted the programming technique mentioned in point 1), I cannot say whether this was the solution to the problem, or instead the previous point. Is there an error ‘regulator or start not set’ in the device log before the error ‘motion generating FB wasn't called for at least one cycle’? I can't answer that right now. By now the machine is running and I am no longer there, at this one. Also, I seem to remember that the 'fbeFBerror' drive structure (5-element array), does not cycle, BUT ONCE THE 5 EVENTS AFTER SWITCHING ON, IT DOES NOT UPDATE ANYMORE (but that's another issue), so diagnostics were not easy.
Last updated: 2024-07-24
Post by rkohser on Scripted Git clone / checkout being blocked by "Project Environment" popup
CODESYS Forge
talk
(Post)
Hi, I am trying to build a CI/CD pipeline around our codesys projects. The only entry point if the git url and branch, as we do not put our project file under source control, so we needed to find a way to git clone from the python scripting engine. This is currently how we do this : system.commands["Git", "Clone"].execute( "ProjectLocation=" + project_dir, "ProjectName=" + project_file_name, "RemoteUrl=" + project_git_remote_url, "GitProjectStoragePath=" + project_git_local_dir, ) system.commands["Git", "Checkout", "Branch"].execute( "PrimaryProjectHandle=0", "BranchName=origin/" + project_git_branch ) This works fine, except that, depending on the environment and the project, the "Project Environment" popup gets displayed to suggest for some updates, and waits for a user interaction, even with the "--noUI" flag injected as parameter. I investigated the VersionUpdateFlags, but the problem is that the git clone is an atomic operation that clones and directly opens the generated project without the possibility to inject any updateFlags argument (only used in the ScripProjects.open() function. I also tried to simulate some keyboard events acknowledge the window from script but I did not find the right location for the SendKeys statement, I think before the git clone call is too early and after is too late. So I am wondering if there would be some other way to do that. Is there some more proper scripting api for the git add on ? Is there a global configuration of the VersionUpdateFlags that would allow the popup to be disabled outside from any project context ? Is there some way to automatically acknowledge this kind of messages in a "--noUI" mode ? What do you suggest ? Thanks for your help, Roland Edit : I managed to solve my problem by following these steps in my pipeline : - create a template of a project and opt file preconfigured not to open the popup - open this project - initialize an empty git repo - add the remote, fetch and checkout the needed branch -> no popup is displayed, hourra Edit2 : The initial question was raised on a CODESYS V3.5 SP18 Patch 2 profile. Since CODESYS 3.5.19.30 a scripting API is available for Codesys Git that allows cloning a project with the support of VersionUpdateFlags https://content.helpme-codesys.com/en/CODESYS%20Git/_git_using_scripting.html
Last updated: 2024-01-19
Post by alex-at-xana on EThercat Dynamic configuration
CODESYS Forge
talk
(Post)
Hi Everyone, For a fast monitoring system I am using Ethercat oversampling and timestamping inputs. As the customers has a lot of different configurations out in the field, we went for a dynamic Ethercat configuration. Already got the whole detection chain working but now I am stuck since two days in debugging the configuration. Here are my quesitons: I extended the dynamic config example for the EL3632 16#0E303052: //EL3632 pSlave^.SetDCSettings(TRUE,TRUE,80,80,TRUE,4000,0,0); pSlave^.AddSyncManager(16#1000,128,16#26,TRUE,3); pSlave^.AddSyncManager(16#1080,128,16#22,TRUE,2); pSlave^.AddSyncManager(16#1100,0,16#4,TRUE,1); pSlave^.AddSyncManager(16#1110,214,16#20,TRUE,0); pSlave^.AddFMMU(0,214,0,7,16#1110,0,1,1); pSlave^.AddFMMU(0,1,0,0,16#80D,0,1,1); xKnown := TRUE; This leads to a PLL Error for these devices. I double and triple checked the configuraton, but cannot find the issue. There seems to be one difference: the config dialog creates a startup parameter: 16#10F3:16#05, Name: Command_0, Value:0. Bitlen: 16, Stop on fault: false, jumpt at fault: false: next line: 0 a) I do not find information on how to set this in my code. Can you help me there? b) Without trying to set the value, I get a PLL error for these devices in the master log. Is the config wrong? c) Do I need to set the Master to Autoconfigure or Manual? I used autoconfigure for my tests. The stack creates input data addresses for the slaves input data range (pSlaves[i].InputData) which are different from those created when I use the Engineering tools dialogs to configure Ethercat. Specifically, the addresses seem to be aligned at 16 byte boundaries when they are created using the engineering tool, but may appear at uneven addresses when I use the script in dynamic config. I did not try to access ULINT at uneven addresses yet, but I am suspecting this may be a problem. Do I need to manually align the addresses ? Any help is appreciated...
Last updated: 2024-09-10
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 mubeta on Some 'pathetic' errors in SoftMotion program
CODESYS Forge
talk
(Post)
Hello everyone, I have a very simple program for the process, but it's driving me crazy and I can't see the problems I'm left with: Short topological description: Dual Core Berghof controller with softmotion runtime version 3.5.19.30; Two axes with servodrive on canopen bus, clocked distributed from master; Ethercat I/O node; 2 ms ethercat task, 2 ms canopen bus cycle time; I/O objects of the canopen master and canopen drives connected to the ethercat task cycle; Problem 1: Two separate programs each manage their own axis and drive, with separate state machines. A first axis moves primarily in velocity, except having to position itself absolutely at a predetermined point at the end of the job; the second axis, on the other hand, is a paper unwinder that changes, for each job cycle, from actions in absolute, relative, and cam displacement with the master axis. Well, the state machine of both axes was written in such a way as to call running the useful FB and change it on state change in this way: CASE i_stateMachine OF 0: o_Power(Enable := TRUE, bRegulatorOn := FALSE, bDriveStart := FALSE, Axis := o_PaperUnwinderAxis); o_MoveAbs(Execute := FALSE, Axis := o_PaperUnwinderAxis); o_MoveRel(Execute := FALSE, Axis := o_PaperUnwinderAxis); o_CamSelect(Execute := FALSE, Master := o_MachineAxis, Slave := o_PaperUnwinderAxis, CamTable := cam_PaperUnwinder); o_CamIn(Execute := FALSE, Master := MachineEncoder, Slave := o_PaperUnwinderAxis); o_CamOut(Execute := FALSE, Slave := o_PaperUnwinderAxis); o_SetPosition(Execute := FALSE, Axis := o_PaperUnwinderAxis); IF ... THEN i_StateMachine := 10; END_IF; 10: o_Power( Enable := TRUE, bRegulatorOn := TRUE, bDriveStart := TRUE, Axis := o_PaperUnwinderAxis ); IF o_Power.Status THEN i_StateMachine := 20; END_IF; 20: (* Avanzamento carta *) o_MoveAbs( Execute := TRUE, Position := o_Somewhere, Velocity := 25.0, Acceleration := 3666.7, Deceleration := 3666.7, Jerk := 48000.0, Direction := MC_DIRECTION.positive, Axis := o_PaperUnwinderAxis ); IF o_MoveAbs.Done THEN o_MoveAbs(Execute := FALSE, Axis := o_PaperUnwinderAxis); i_StateMachine := 30; END_IF 30: d_HomingPosition := ...; o_SetPosition( Execute := TRUE, Position := d_HomingPosition, Mode := FALSE, Axis := o_PaperUnwinderAxis ); (* ... *) IF o_SetPosition.Done = TRUE THEN o_SetPosition(Execute := FALSE, Axis := o_PaperUnwinderAxis ); o_LogServer.Append(sMessage := '...', lscClass := LOGSERVER_CLASS.ALWAYS, sdt := o_CommonsMgrData.systime.sdtLocal); i_StateMachine := 40; END_IF; 50: ... The code above is a sketchy example of what I wanted to write. But it gives me a spot problem: in some, the state change results in a drive error, which is unrecoverable except with a reinitialization via SM3_ReinitDrive(). Things are improved a little if in the program I always run the call of all softmotion blocks in this way: o_Power(Axis := o_PaperUnwinderAxis); o_Jog(Axis := o_PaperUnwinderAxis); o_Halt(Axis := o_PaperUnwinderAxis); o_MoveAbs(Axis := o_PaperUnwinderAxis); o_MoveRel(Axis := o_PaperUnwinderAxis); o_CamIn(Master := MachineEncoder, Slave := o_PaperUnwinderAxis); o_CamOut(Slave := o_PaperUnwinderAxis); If I don't execute all the calls of all the motion FBs used, when exchanging machine state often (but not always), the axis goes into error with event id THE_FB_WASNT_CALL... Done a little diagnostics it seems that the FBs return the bDone, before they are completely terminated. I tried doing the machine state exchange not with the bDone bit of the FBs, but with the 'standstill' state of the axis. It didn't seem to change anything. Problem 2: During the use SM3_ReinitDrive() I get the erro in the log: "NetID 0: SDO read error for object 16#607C..." Assuming that the device involved it's one of the two servodrive, (no others device are present in the network), I don't found any object 0x607C in the 'possible object list in/out' of the two drive, and I don't understand where this object can be listed. So any ideas and suggestions regarding these two issues will be very, very welcome. If you need the source project, I am willing to send it.
Last updated: 2024-07-17
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
To search for an exact phrase, put it in quotes. Example: "getting started docs"
To exclude a word or phrase, put a dash in front of it. Example: docs -help
To search on specific fields, use these field names instead of a general text search. You can group with AND
or OR
.