Accessing PLC metadata from Codesys (ABB AC500 PLC) - plc

I have a single PLC program that will be run on multiple ABB AC500 PLCs. I need each PLC to have a very slightly different behaviour (limited to selecting an integer value unique for each PLC, to allow timing of a specific event to be different on each PLC).
To allow a single, identical program to be maintained and uploaded to the multiple PLCs, the strategy I have in mind is to access a piece of unique metadata about the PLC to determine the correct value for the specific PLC.
Reasonable information might include the IP address assigned to the PLC, or perhaps the PLC serial number.
I've looked carefully but cannot find a way of accessing this information at runtime - I'm guessing there is a straightforward function in a module that will return one or other of these pieces of info?
Or perhaps there is a better way of having this kind of PLC-specific behaviour?
Thank you!

If you want specific behavior to be binded by hardware you can use DI module. You can take 3 inputs and use them as bits. You can differ 7 PLCs with 3 inputs like 3 bits by using wire jumpers.
This way will be better because if one want to change PLC for whatever reason, you do not need to change programm and change serial number or such. It will work with any PLC.
For example if you have
| DI | PLC1 | PLC2 | PLC3 |
|-----|------|------|------|
| DI1 | 0 | 1 | 0 |
| DI2 | 0 | 0 | 1 |
| DI3 | 0 | 0 | 0 |
And so on.
But I would simply use constant variable in programm and change it before programm download.

Quicly looking at Automation Builder help files (from here), it seems that there should be a library called Internal System Library (SysInt_AC500_Vx.lib).
The library includes a function block called SLOT_PROD_ENTRY_READ, which "reads one line from the production data in the Flash memory of a Communication Module." You can find it using the search function in the manual.
One of the values that can be read is serial number. Another solution would be use for example MAC address. I don't have an ABB PLC to try on, and the documentation doesn't include a very clear example. But it should be possible. Note the the following is not tested.
​
​VAR
instance : CPU_PROD_ENTRY_READ; //Might need a library namespace
serial : STRING(80);
END_VAR
instance(
EN := TRUE,
SLOT := 0,
SECTION := 'Common',
KEY_SEARCH​ := 'SERIAL_NR',
INDEX := 0,
ACT := FLASH_DATA_READ
);
IF instance.DONE THEN
serial := instance.VALUE;
instance(EN := FALSE);
END_IF

use the following snippet for the mac address.
Variable :
diNumber: DINT;
iAdapter: DINT;
stName: STRING(255);
stDescription: STRING(255);
aby_address:ARRAY[1..6]OF BYTE;
Program:
getnumberofadapters(ADR(diNumber));
FOR iAdapter := 0 TO diNumber DO
getadapterinfo(
iAdapterNum := iAdapter,
pbName := ADR(stName),
pbDescrition := ADR(stDescription),
iBuffersize := SIZEOF(stName),
iMacLength := 6,
sMacAdress := ADR(aby_address));
END_FOR

Related

How to make Windows' Bonjour resolve foo.bar.local subdomains created by Avahi

Why can't Windows' Bonjour (the Apple one) automatically resolve foo.bar.local, when Ubuntu and macOS can?
foo.local instead is resolved without issues by every OS.
Here's my avahi-daemon.conf:
[server]
host-name=foo
domain-name=bar.local
...
This discussion mentions that Windows' Bonjour implementation does not support aliases, is this the culprit?
How does this tool differ from my solution?
EDIT: I don't want to set an alias. foo.bar.local is different from bar.local.
I just want to have different hostnames under the same "domain".
For example, foo.bar.local is 192.168.0.8 while foo1.bar.local is 192.168.0.9.
I won't have foo.local, bar.local and foo.bar.local all in the same network. I will use foo.bar.local, with only foo varying (*.bar.local)
From my current findings, this seems to be intentional. Excerpt from the source code (mDNSResponder-878.30.4, function NSPLookupServiceBegin in mdnsNSP.c):
// <rdar://problem/4050633>
// Don't resolve multi-label name
// <rdar://problem/5914160> Eliminate use of GetNextLabel in mdnsNSP
// Add checks for GetNextLabel returning NULL, individual labels being greater than
// 64 bytes, and the number of labels being greater than MAX_LABELS
replyDomain = translated;
while (replyDomain && *replyDomain && labels < MAX_LABELS)
{
label[labels++] = replyDomain;
replyDomain = GetNextLabel(replyDomain, text);
}
require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND );
It returns an error if the name has more than two labels, as in the case of foo.bar.local.
In my tests, I just removed the last line. With the new build, it resolved names with multiple labels successfully. I did not yet encounter any side-effects so far.
Has anybody an idea about the intention behind not resolving multi-label names?

WSOCK32.DLL htons function

In a Visual FoxPro app using sockets, we are using wsock32.dll and use the htons() function to convert a portnumber to TCP/IP network byte order. It should return an unsigned short between 0 and 65535. When testing this with port 63333 it returns 26103 but after installing the Windows Fall Creators update it returns a bigger value: 16213495.
Sample FoxPro program:
DECLARE INTEGER htons IN "wsock32.dll" INTEGER hostshort
LOCAL portNumber, htonsNumber
portNumber = 63333
htonsNumber = htons( portNumber )
? htonsNumber
The resulting value should go into a "sockaddr" structure used by the connect() function but there is only space for 2 bytes for the port.
Does anyone know what has happened in this windows update to the wsock32 functions and/or has a suggestion to solve this?
I compared the Windows 10 FCU function with Windows 8 and Windows has reordered the register usage and saved one AND instruction. This is most likely a compiler optimization and not a source code change. Because the left-shifted half is not masked you get garbage in bits 16-23 but these bits should be ignored. The function is still correct for anyone that follows the Windows ABI.
The best solution is to update the function declaration so it uses a 16-bit integer type. If that is not possible you can cast the number to a 16-bit type in languages that support casting. The final option is to truncate the value yourself by ANDing with 0xffff:
htonsNumber = BitAnd(htons(portNumber), 0xffff)
SHORT is listed as a valid return type so that should work as well:
DECLARE SHORT htons IN "wsock32.dll" INTEGER

OpenModelica updating CombiTable input in real time

I have a model in which I am using a CombiTable1D to retrieve an external input from a .txt file. The file is generated by a Python script at the moment but in the final phase of the project, it is going to be updated each second. For now, simulation takes place without a problem as the .txt file is static. Just read the file and make simulation according to the data written there.
What I want to do is to simulate a model until a certain time, let's say 100s, and then make it wait until a real time event by which the .txt file is updated for the next external input values between 100-200. The simulation should continue by getting these new values for the next 100 seconds.
As I have already been working with OMPython, it is really practical for me to edit the .txt file using Python, let's say for each 10 seconds in real time. I can now simulate the model until the time instance that I define as the refreshing point of the external input. But I couldn't figure out how to keep the state of the simulation and make it read the file once again.
Actually, this sounds like a co-simulation scenario to me. Anyway, what you could do is to extend from CombiTable1D and have something like
block CombiTable1DWithUpdate
extends Modelica.Blocks.Tables.CombiTable1D(final tableOnFile=true);
algorithm
when sample(0, 10) then
readTableData(tableID, /* force update */ true, verboseRead);
end when;
end CombiTable1DWithUpdate;
In addition the the answer I accepted, I want to give another solution which is not so efficient. For a simple model with a capacitor and resistor, I made successfull tests but with more complex models, it doesn't function properly. In a Modelica script, realTimeSimulation.mos:
outputFile := "stepResult.mat";
simulation_step := 1;
start_time := 0;
stop_time := start_time+simulation_step;
loadFile("WhereverTheFileIs.mo");
buildModel(myTestModel);
system("myTestModel-override=startTime="+String(start_time)+",stopTime="+String(stop_time)+" -r="+outputFile);
will build the model and simulate the first step until the simulation time t=1s. Later on, using Python the text file is updated. The new data for the time between t=1s and t=2s are written to the text file where I am getting the input of the model. Then another step of the simulation is made for the time between t=1s and t=2s. As a loop, it continues like forever like: actualize the data, make new simulation for the new time interval. The trick is, reading the output file created at the end of each step and giving all the variable values as the new initial conditions to the simulation, using following script:
valueList := readSimulationResultVars(outputFile);
start_time := start_time+simulation_step;
stop_time := stop_time+simulation_step;
value := val(OpenModelica.Scripting.stringVariableName(valueList[1]),start_time,outputFile);',
variableString := valueList[1] + "=" + String(value);
for i in 2:size(valueList,1) loop
value := val(OpenModelica.Scripting.stringVariableName(valueList[i]),start_time,outputFile);
variableString := variableString + "," + valueList[i] + "=" + String(value);
end for;
system("myTestModel-override startTime="+String(start_time)+",stopTime="+String(stop_time)+",variableString+" -r="+outputFile);

Linking multiple custom variables in Omniture

I have an implementation in Sitecatalyst, where i have to track categories and the multiple tags associated with the categories. How should i go for it. What should be the variables which should be defined for it in omniture.
for example -
|---------------------|
| MOOD | // Main Category
|---------------------|
| Uplifting | // Sub Category
|---------------------|
| Fun | // Sub Category
|---------------------|
| Proud | // Sub Category
|---------------------|
| Fun | // Sub Category
There are three options:
Listprop
You could create a listprop which you can enable in the report suite settings by implementing a delimiter for the traffic variable. This is especially handy if you have the possibility to create multiple levels. You can then implement the traffic variable like so:
s.prop1 = "MOOD|Uplifting|Fun|Proud|Fun";
Adobe Analytics will automatically split the values based on the pipe character. Please note that listprops don't allow correlations and pathing.
Multiple traffic variables/props
The other option would be to create multiple traffic variables and define all of them on every page where they're required.
s.prop1 = "MOOD";
s.prop2 = "Uplifting";
s.prop3 = "Fun";
s.prop4 = "Proud";
s.prop5 = "Fun";
However, this option will consume a lot of traffic variables of which you only have 75.
Classification
The third option would look the same as the listprop, however, you don't configure the traffic variable as a listprop, you configure it as a normal traffic variable and classify it later on using the Classification Rule Builder.
Using the classification rule builder you can split the incoming data by the pipe character (using regular expression) and create new dimensions resembling the categories.
s.prop1 = "MOOD|Uplifting|Fun|Proud|Fun";
I would personally go for the third option as it doesn't require a lot of props and it allows for a future proof approach of measuring the categories even when you're adding more levels.
Good luck with your implementation!

About iteration in Selenium IDE

I am newbie/beginner to Selenium IDE, i am running a test on a web application.
I wish to have a input of username into a textbox, let say 100 times how could i done this?
What i should type in the source or value column?
I want result somehow similar to
for (i=1;i<=100;i++)
{
value = "user" + i ;
}
(but i have no idea how to do it ...)
Then the test will going to loop 100 times and everytime will input different username like
User1 .... first time
User2 .... second time
Any helps would be appreciated. Thank you very much!
This approach should also work as well, using the SelBlocks extension.
for | i=1; i <= 100; i++ |
type | id=fieldname |user ${i}
endFor| |
You need to use parametrization with looping, you can refer
Selenium IDE: How to Pass Variables