Write bytes to a PLC device - vb.net

I'm working around a connection between a PLC device and my companies PC. The PLC is the known Siemens S7-200 and I'm using vb.NET. Probably I should use another language but vb.NET is the one I'm more comfortable with. To do so, I'm also using a PPI protocol through COM1 and LibNoDave library to establish the connection.
The program I'm testing has to have Input 0.0 On, so I attached a switch to make it happen. Also I made a vb console to read (and write) the state of the Inputs and Outputs (as the LED physical indicators on the device) and also the state of the Bit memories:
The console reader (LEITOR section - sorry) is working like I intended and all the Q's, I's and M's are correctly lighting up if it is the case.
The problem is, to run the PLC program, I also have to lit up Q 1.1.
The Ladder Network that describes this has the following logical map:
I know I have to use the code:
Public FDS As libnodave.daveOSserialType 'Serial type
Public DI As libnodave.daveInterface 'Interface
Public DC As libnodave.daveConnection 'Connection
Public lPPI As Integer = 0 'Local
Public pPPI As Integer = 2 'PLC
Public RES As Integer = 0 'Response
Public REP As Integer = 0 'Response
Public buf(100) As Byte
Sub Code()
FDS.rfd = libnodave.setPort("COM1", "9600", AscW("E"))
DI = New libnodave.daveInterface(FDS, "IF1", lPPI, libnodave.daveProtoPPI, libnodave.daveSpeed93k)
DI.setTimeout(1000000)
DC = New libnodave.daveConnection(DI, pPPI, 0, 0)
RES = DC.connectPLC
'Write on PLC:
RES = DC.writeBytes(...
End sub
The code is working fine with no errors and an establish connection (until the last RESponse).
Here's the problem:
I can lit up the Output 1.1 (on the device and on the console) by doing the following:
RES = DC.writeBytes(libnodave.daveDB, 1, 1500, 16, buf)
where
buf = BitConverter.GetBytes(libnodave.daveSwapIed_16(30))
by repeating these two steps five more times (another time with 30 again, two more times with 50 and, finally, another two times with 50).
I'm pretty sure I'm doing something wrong, but there's not a lot of these commands description available online for a guy like me (who's just got started).
Can anyone explain what's going on? And also, How can I lit Q 1.1 with just one step?

Related

MQ PCFParameter returning different values for Linux and Windows

When using IBM PCF Messages to monitor a queue, getting values of Input Count (MQIA_OPEN_INPUT_COUNT), it works perfectly for MQ Servers installed in Windows environment, but not for Linux. Not sure if it is a code or environment issue.
If we connect to a Windows service and perform que query there are more parameters in the response if compared to the Linux.
Same code, different results. Not sure if it is a configuration on the Channel, permissions or any other environment issue. On both MQ Servers the queues are local.
I've tried using IBM.WMQ.MQC.MQCMD_INQUIRE_Q_STATUS, with no success. Didn't find any workaround to get MQIA_OPEN_INPUT_COUNT.
PCFMessages documentation is very limited, so I didn't find anything related to this problem at MQIA_OPEN_INPUT_COUNT documentation:
https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.ref.adm.doc/q087810_.htm
Any idea of how to solve this?
Public Function GetQtyQConnections(ByVal MQQueueName As String) As Integer
Dim queueManager As IBM.WMQ.MQQueueManager = Nothing
queueManager = New IBM.WMQ.MQQueueManager(AppSettings("MQQueueManagerName"), AppSettings("MQChannelName"), AppSettings("MQConnectionName"))
Dim oPCFMessageAgent As IBM.WMQ.PCF.PCFMessageAgent = New IBM.WMQ.PCF.PCFMessageAgent
oPCFMessageAgent.Connect(queueManager)
Dim pcfMsg As IBM.WMQ.PCF.PCFMessage = New IBM.WMQ.PCF.PCFMessage(IBM.WMQ.MQC.MQCMD_INQUIRE_Q)
pcfMsg.AddParameter(IBM.WMQ.MQC.MQCA_Q_NAME, MQQueueName)
Dim pcfResponse() As IBM.WMQ.PCF.PCFMessage = oPCFMessageAgent.Send(pcfMsg)
Dim pcfResponseLen As Integer = pcfResponse.Length
Dim inputcount As Integer = -1
For i As Integer = 0 To pcfResponseLen - 1
Dim oParams() As IBM.WMQ.PCF.PCFParameter = pcfResponse(i).GetParameters
For Each oParam As IBM.WMQ.PCF.PCFParameter In oParams
Select Case oParam.Parameter
Case IBM.WMQ.MQC.MQIA_OPEN_INPUT_COUNT
inputcount = Integer.Parse(oParam.GetValue())
End Select
Next
Next
Return inputcount
End Function
On Windows:
---------------
2016-QUEUENAME
20-1
134--3
2027-2018-03-12
2028-13.59.40
2019-
22-0
2030-
2029-
2124-
96-0
95-0
98--3
2004-2018-03-12
2005-13.59.40
3-0
2119-
61-0
6-0
5-1
184-1
188-0
4-2
7-1
2013-
34-0
9-0
8-1
272-2
2008-
17-0
15-5000
13-104857600
123--3
16-0
24-0
78-0
18-0
2012-
10-0
190-0
40-80
41-20
43-0
44-0
42-1
46-0
54-999999999
21-999999999
45-1
23-1
128--3
2023-
29-1
26-0
28-1
12-0
On Linux:
---------------
2016-QUEUENAME
20-6
2027-2019-03-11
2028-17.38.24
2030-
2029-
96-0
95-0
2119-
61-1
6-0
5-1
184-1
2013-QUEUEDESCRIPTION
10-0
2017-QUEUEMANAGER
2018-QUEUENAME
45-1
2024-QUEUEMANAGER
From your output I can see that the queue you have looked at on Windows is a local queue. The second parameter you display (20) is MQIA_Q_TYPE and it has a value of (1) MQQT_LOCAL.
The queue you have looked at on Linux however is a remote queue. It's MQIA_Q_TYPE (20) parameter has a value of (6) MQQT_REMOTE.
There are many differences between local queues and remote queues, and their attributes are quite different. Try using runmqsc and display a few local and remote queues to understand the differences. These differences have not occurred because of the different platform, just because of the different queue type.
You say in your question that on both MQ Servers the queues are local, but I'm afraid that is not what your output is showing.
Also, if you want to use the Inquire Queue command, please be sure you know that OpenInputCount and OpenOutputCount are only shown for local queues, not remote queues.
If your Linux and Windows releases of IBM MQ are at the same version level then you should get the same response parameters returned.
Why don't you format your output so that it is readable to the average human? Nobody will know what you mean by 2016, 2028, etc. (except for me and a few others)
Issues:
You did not specify the "Status Type" for the request. i.e. QUEUE vs HANDLE
You did not specify any attributes for the request.
Have a look at MQListQueueStatus01.java code I posted here: IBM MQ fetch LGETTIME using Java
Finally, why don't you use C# rather than VB? You could simply use all of the Java/MQ/PCF code that I post since C# is a clone of Java (so to speak).

getgroup() is very slow

I am using the function getgroup() to read all of the groups of a user in the active directory.
I'm not sure if I'm doing something wrong but it is very very slow. Each time it arrives at this point, it takes several seconds. I'm also accessing the rest of Active directory using the integrated function of "Accountmanagement" and it executes instantly.
Here's the code:
For y As Integer = 0 To AccountCount - 1
Dim UserGroupArray As PrincipalSearchResult(Of Principal) = UserResult(y).GetGroups()
UserInfoGroup(y) = New String(UserGroupArray.Count - 1) {}
For i As Integer = 0 To UserGroupArray.Count - 1
UserInfoGroup(y)(i) = UserGroupArray(i).ToString()
Next
Next
Later on...:
AccountChecker_Listview.Groups.Add(New ListViewGroup(Items(y, 0), HorizontalAlignment.Left))
For i As Integer = 0 To UserInfoGroup(y).Count - 1
AccountChecker_Listview.Items.Add(UserInfoGroup(y)(i)).Group = AccountChecker_Listview.Groups(y)
Next
Item(,) contains my normal Active directory data that I display Item(y, 0) contain the username.
y is the number of user accounts in AD. I also have some other code for the other information in this loop but it's not the issue here.
Anyone know how to make this goes faster or if there is another solution?
I'd recommend trying to find out where the time is spent. One option is to use a profiler, either the one built into Visual Studio or a third-party profiler like Redgate's Ants Profiler or the Yourkit .Net Profiler.
Another is to trace the time taken using the System.Diagnostics.Stopwatch class and use the results to guide your optimization efforts. For example time the function that retrieves data from Active Directory and separately time the function that populates the view to narrow down where the bottleneck is.
If the bottleneck is in the Active Directory lookup you may want to consider running the operation asynchronously so that the window is not blocked and populates as new data is retrieved. If it's in the listview you may want to consider for example inserting the data in a batch operation.

To many IF's. Is there a better way?

I have this code:
If CheckBox1.Checked = True Then
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\1\Pc - S.txt", "Pc - S - A.txt")
'Sleep(2000) ' to sleep for 2 second
System.Threading.Thread.Sleep(2000)
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\1\Pc - S - A.txt", "Pc - S.txt")
End If
If CheckBox2.Checked = True Then
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\2\Pc - S.txt", "Pc - S - A.txt")
'Sleep(2000) ' to sleep for 2 second
My.Computer.FileSystem.RenameFile("C:\Users\mario\Desktop\Dominio\2\Pc - S - A.txt", "Pc - S.txt")
End If
And it continues with another 24 if's. I need them to change the name all at the same time and then wait two seconds and change it again all at the same time.Will i need to have 48(24+24) IF's?
Explanation:
Im creating a super simple way of shuting down the computers at my school. Here is my idea(hope you can understand it)
I put a program at every computer at school (in the regestry at the startup) that does the following:
(Atencion not really in a programing language)
for(1){
sleep 1000 // wait 1 sec
if.FILE.EXISTS(\\domain\folder1\shutdown.txt) then{
do: shutdown pc // if file exist it shutdowns the pc
}
}
So what this does is if there is a file in a certain directory in the domain, it shutsdown the pc.
Im doing this for 24 pcs, so i need to shutdown them individualy, therefor the many folders.
So i could simply rename the files by hand, but for 24 pcs that is a lot of work. So im developing a aplication that asks me what pcs i want to shutdown and then does:
1: Rename file.
2: Wait 2 or more seconds (so the computers in the network have time to verify if there is such file)
3: rename back the file so when the pc restarts it doesnt shutdown again.
Did you get what im trying to do? And yes there are more orthodox ways of doing this. but this is really simple, and this is more of a test than anything. (Later will be using more proper ways of doing this and do other options over the network)
Since the only difference is a single number in the filepath, why not do something like this?
var dict = new Dictionary<CheckBox, string>();
dict.Add(CheckBox1, "1");
dict.Add(CheckBox2, "2");
//etc
foreach (var kvp in dict)
{
if(kvp.Key.Checked == true)
{
My.Computer.FileSystem.RenameFile(String.Format("C:\Users\mario\Desktop\Dominio\{0}\Pc - S.txt", kvp.Value ), "Pc - S - A.txt")
}
}
We create a generic dictionary to store the association of the checkboxes to the string/number and then loop through the dictionary, checking the values. We use a format string (you could use string concatenation instead) to insert the string/number into the filepath string.
Note, this is in C#, as I'm not a VB programmer. The syntax will likely be slightly different.
Since your intention is to remotely shut down the PC's, why not actually shut them down from your code, rather than some hackish "create files then wait 2 seconds" approach?
Again, this is C# but the translation should be pretty straightforward:
var ComputerNames = new List<string>() {"Computer1", "Computer2", "Computer3"};
foreach(var pc in ComputerNames)
{
Process.Start("shutdown","-s -m \\\\" + pc);
}

GP-635T GPS module dropping data

I have a GP-635T GPS module attached to my Arduino UNO (GPS TX->UNO RX[Pin 0]) using Serial.
Then I just read the incoming data byte for byte adding them to a string as they are read and when I reach a newline character(13) I just print the complete String of data and reset the data String for the next run.
Code:
void setup()
{
Serial.begin(9600);
Serial.println("Initialized Serial port..");
}
String data = "";
void loop()
{
while(Serial.available())
{
char gpsByte = Serial.read();// Read a byte from the GPS
data += gpsByte;
if(gpsByte == 13){
Serial.print(data);
data = "";
}
}
delay(100);
}
This code works as it is. But the problem here is the data I get from it.
The module prints out multiple datalines at 1Hz using the NMEA-0183 standard. I do not have any idea how it decides what to print when because the data is not consistent for more than 5 "prints" of the (almost)complete dataset.
Ex:
$GPGGA,213948.00,,,,,0,00,99.99,,,,,,*63
$GPGSA,A30 <--- A30?
$GPGSV,1,1,01,29,,,30*70
$GPGLL,,,,,213948.00,V,N*4F
$GPRMC,213949.00,V,,,,,,,290314,,,N*74
$GPVTG,,,,,,,,,N*30
$GPGGA,213949.00,,,,,0,00,99.99,,,,,,*62
$GPGSA,A,1,,,,PGSV,1,1,01,29,,,30*70 <--- PGSV?
$GPGLL,,,,,213949.00,V,N*4E
$GPRMC,213950.00,V,,,,,,,290314,,,N*7C
$GPVTG,,,,,,,,,N*30
$GPGGA,213950.00,,,,,0,00,99.99,,,,,,*6A
...
$GPGGA,214045SA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,01,29,,,30*70$GPRMC,214046.00,V,,,,,,,290314,,,N*75 <--- Missing linebreak?
$GPVTG,,,,,,,,,N*30
$G00,99.99,,,,,,*63 <--- $G00??
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,02,20,,,27,29,,,30*74
$GPGLL,$GPRMC,214047.00,V,,,,,,,290314,,,N*74 <--- Missing data?
$GPVTG,,,,,,,,,N*30
$GPGGA,214047.00,,,,,0,00,99.99,,,,,,*62
$,99.99*30 <--- Alot of missing data
I think you get the point, I have nothing but the GPS connected to the Arduino.
I have tried with sending the data wirelessly with some wireless modules with no change.
I have tried with using AltSoftSerial and SoftwareSerial but I got messier data with the latter.
I have tried different baud rates with no luck.
Does anyone have any idea as to why this is happening? The module is brand new and I have no idea why it would behave like this.
NOTE: I do not have a very good GPS signal to my module from within my house, I did try going outside with it and see if I could get a signal which I did from about 3 satellites, but that didn't change the output.
Well this was a very strange problem, The problem did go away when I shortened the delay from 100ms to 10ms.
It might have to do something with the buffer and maybe it just gets overflowed before I start reading bytes from it.
However if someone else have got this same problem, shorten the loop delay and you should be fine!

label to display graphics card or total system ram through registry possibly?

hi all i have this great code that i love that will display the kind of processor model and speed like so
RegistryKey Rkey = Registry.LocalMachine;
Rkey = Rkey.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
Labelproc.Text = (string)Rkey.GetValue("ProcessorNameString");
and i was wondering if theres a way to do this for the kind of graphics card and the total installed system ram (in separate labels)
Rather than reading the registry I'd suggest that you use WMI, specifically Win32_ComputerSystem to find number of CPUs and Win32_Processor to find info about it and Win32_ComputerSystem.TotalPhysicalMemory would give the RAM.
I'm sure there is some way to pick up the graphics card(s) as well (remember that there might be more than one).
Here's an article with some samples for getting out various data:
http://msdn.microsoft.com/en-us/library/aa394587%28VS.85%29.aspx
The samples are in vbscript but it's similar, I think the C# code for getting the RAM would be something like:
ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem") ;
ManagementObjectCollection coll = query.Get();
foreach( ManagementObject mo in coll )
{
Console.WriteLine(mo["totalphysicalmemory"].ToString());
}