Get OPCItem from ClientHandles in DataChange Event - OPCAutomation.dll - vb.net

Is it possible to get the OPCItem from a Group using the ClientHandle value?
Pseudo example:
Dim Server As OPCServer
Dim Groups As OPCGroups
Dim WithEvents Group1 as OPCGroup
Dim ItemGroup as OPCItems
Dim Item as OPCItem
Private Sub Form1_Load(sender as Object, e as EventArgs) Handles MyBase.Load
Server.Connect("MyServer")
Group1 = New OPCGroup()
Group1.Name = "Group1"
Group1.IsSubscribed = True
Group1.OPCItems.Add("Item1", 1)
Server.OPCGroups.Add(Group1)
End Sub
Private Sub Group1_DataChange(TransactionID As Integer, NumItems As Integer, ByRef ClientHandles As Array, ByRef ItemValues As Array, ByRef Qualities As Array, ByRef TimeStamps As Array) Handles Group1.DataChange
Dim Browser as OPCBrowser = Server.CreateBrowser()
Browser.ShowBranches()
Browser.DataType = vbInteger
Browser.ShowLeafs()
Dim qualityValue as Integer
For q As Integer = 1 To Qualities.Length
qualityValue = Qualities.GetValue(n)
If qualityValue = 192 Then
'HERE is where I want to get the OPCItem by using the ClientHandles
'I can use the ClientHandle to get the Value, but I'd also like to get the ItemID to do validation against.
Dim itemClientHandle as Integer = Convert.ToInt32(ClientHandles.GetValue(n))
Dim itemValueByClientHandle as String = ItemValues.GetValue(n).ToString()
End If
Next
'It is possible to use the OPCBrowser to get the Item Names but how do I correlate the two?
Dim itemNames as New List(Of String)
For n As Integer = 1 To OPCBrowser.Count
itemNames.Add(OPCBrowser.Item(i))
Next
' Do More Stuff
End Sub
There is a request to enhance an existing client application to be paused when a value is sent from the client to the OPC Server and wait for the bit tag to switch as in indication the process has finished. This part is built; however, there are multiple items (tags) in the group and there needs to be a way of identifying the correct items bit value was changed.

Related

Is there a way to retreive the collection items from Textbox - AutoCompleteCustomSource in vb.net

On executing the below code, I am successfully able to insert an array values into a Textbox AutoCompleteCustomSource.
But I need to be able to read all data back from the AutoCompleteCustomSource too, and put it into an array.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.AutoCompleteMode = AutoCompleteMode.Suggest
TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
Dim Arr1 As String() = {"Hello", "How", "Are", "You"}
'Below line of code puts all the array values into TxtBox.AutoCompleteCustomSource
TextBox1.AutoCompleteCustomSource.AddRange(Arr1)
'-------------Need Help on Below-------
'How to read all data from TextBox1.AutoCompleteCustomSource and bring it into an array
Dim MyArr1
MyArr1 = TextBox1.AutoCompleteCustomSource??????????????????????????????????????
End Sub
You can throw a bit of LINQ at it:
Dim items = TextBox1.AutoCompleteCustomSource.Cast(Of String)().ToArray()
The AutoCompleteCustomSource is type AutoCompleteStringCollection, which implements IEnumerable but not IEnumerable(Of T), although every item is guaranteed to be a String. That means that you can call the Cast(Of String) extension method to get an IEnumerable(Of String) and then call ToArray on that to get a String array.
Other options include this:
Dim source = TextBox1.AutoCompleteCustomSource
Dim items(source.Count - 1) As String
source.CopyTo(items, 0)
or you can go really old-school with this:
Dim source = TextBox1.AutoCompleteCustomSource
Dim upperBound = source.Count - 1
Dim items(upperBound) As String
For i = 0 To upperBound
items(i) = source(i)
Next

Listbox Sorting Issue

I am having trouble sorting a listbox the way I am expecting it to work even using the sorted = true property. Assuming i have files named as such, when i sort by "Name" in a Windows Folder view (outside of vb) it sorts like so:
1180741
1179715
1162371
1141511
1131750
1117362
1104199
1082698
1062921
1043875
991514
970621
963154
952954
948067
917669
904315
899902
892398
882024
This is how i need it to be sorted in my list. However, with the sorted = true property, vb.net decides to sort like this:
1043875
1062921
1082698
1104199
1117362
1131750
1141511
1162371
1179715
1180741
882024
892398
899902
904315
917669
948067
952954
963154
970621
991514
I cannot understand why windows sorts the list correctly but VB does not. Any help would be most appreciated.
*This example assumes that each object in the listbox is a string representation of an integer, if in the case that it is not, the item will be skipped, you can modify this to handle such a case in another way if you want....
Example:
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Dim r As New Random(Now.Millisecond)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'We simulated a populated listbox
For I As Integer = 0 To 1000
Dim n As Integer = r.Next(0, Integer.MaxValue)
ListBox1.Items.Add(n.ToString)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SortListBoxNumbers(ListBox1)
End Sub
Sub SortListBoxNumbers(listbox As ListBox)
Dim numbers As New List(Of Integer)
For Each item As Object In listbox.Items
Dim n As Integer = 0
If item.GetType = GetType(String) Then
If Integer.TryParse(DirectCast(item, String), n) Then
numbers.Add(n)
End If
End If
Next
listbox.Items.Clear()
numbers = SortNumbers(numbers)
For Each n As Integer In numbers
listbox.Items.Add(n.ToString)
Next
End Sub
Function SortNumbers(numbers As List(Of Integer)) As List(Of Integer)
Dim result As New List(Of Integer)
Dim count As Integer = numbers.Count
Do
Dim highN As Integer = Integer.MinValue
For Each n As Integer In numbers
If n > highN Then highN = n
Next
numbers.Remove(highN)
result.Insert(result.Count, highN)
Loop Until result.Count = count
result.Reverse()
Return result
End Function
End Class

Adding multiple items to combo box only adds LAST ITEM

I'm trying to add multiple items to a combobox but only the last item is showing.
Example:
Dim i as Integer
For i = 0 to 3
AddItemToComboBox(i, i)
Next
Sub AddItemToComboBoxMod(ByVal itmValue, ByVal itmData)
Dim comboSource As New Dictionary(Of String, String)()
comboSource.Add(itmValue, itmData)
cbComboBox.DataSource = New BindingSource(comboSource, Nothing)
cbComboBox.DisplayMember = "Value"
cbComboBox.ValueMember = "Key"
Dim key As String = DirectCast(cbComboBox.SelectedItem, KeyValuePair(Of String, String)).Key
Dim value As String = DirectCast(cbComboBox.SelectedItem, KeyValuePair(Of String, String)).Value
End Sub
BUT at the end, only the last item "3" will be there. 0,1,2 will be missing.
Why?
You're making this too complicated.
Populate the values you want in the list.
THEN set the DataSource property ONCE. In above code, you are setting the DataSource property in each iteration of the loop.
Below is a sample event handler that I put together to demonstrate the concept.
Private Sub cmdPopulate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdPopulate.Click
Dim i As Integer
Dim comboSource As New Dictionary(Of String, String)()
For i = 0 To 3
comboSource.Add(i, i)
Next
cbComboBox.DataSource = New BindingSource(comboSource, Nothing)
cbComboBox.DisplayMember = "Value"
cbComboBox.ValueMember = "Key"
End Sub
It looks to me like you are creating a new Dictionary every time you call AddItemToComboBoxMod(). You might want to instantiate it and bind it somewhere else. The add item method should only be appending the key value pair.

I want my structure of vehicle makes to show in a listbox

I'm not sure what to change in my code to make the listbox show each vehicle make.
Public Class Form1
Structure Vehicle
Dim Make As String
Dim Model As String
Dim Doors As Integer
Dim Hp As Integer
Dim VIN As String
End Structure
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Dim Vehicles(9) As Vehicle
Vehicles(0).Make = "Chevrolet"
Vehicles(1).Make = "Dodge"
Vehicles(2).Make = "Nissan"
Vehicles(3).Make = "Mitsubishi"
Made a for loop to show each make
For i = 0 To 4 Step 1
ls.Items.Add(Vehicles(i).Make.ToString)
Next
End Sub
You're only supplying data to 4 instances (0-3):
Vehicles(0).Make = "Chevrolet"
Vehicles(1).Make = "Dodge"
Vehicles(2).Make = "Nissan"
Vehicles(3).Make = "Mitsubishi"
But you're trying to retrieve data from 5 instances (0-4):
For i = 0 To 4 Step 1
String is by default Nothing unless you assign it a value. So your 5th instance is an empty instance of Vehicle. Thus...
This will fail because Vehicles(4).Make is Nothing, and you can't call a method (ToString) on Nothing:
ls.Items.Add(Vehicles(i).Make.ToString)
And this will fail because Vehicles(4).Make is Nothing, and you can't add an empty or null string to a listbox:
ls.Items.Add(Vehicles(i).Make)
You need to either:
Adjust your loop to only cover the data you have (For i = 0 To 3 Step 1), or
Add another data element (Vehicles(4).Make = "Something"), or
Add null checking to your code (If Vehicles(i).Make Is Not Nothing)
Why don't you try using Class instead of Structure, and using list instead of array. Since it seems that yourself also don't how long will the data be, so in this case list is a good choice for your and you can just consider it as a dynamic array in c/c++. Following is my way to interpret
Public Class Form1
Class Vehicle
Dim Make As String
Dim Model As String
Dim Doors As Integer
Dim Hp As Integer
Dim VIN As String
Public Sub New(Dim s As String)
Me.Model=s
End Sub
End Class
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Dim Vehicles As New List(Of Vehicle)
Vehicles.Add(New Vehicle("Chevrolet"))
Vehicles.Add(New Vehicle("Dodge"))
Vehicles.Add(New Vehicle("Nissan"))
Vehicles.Add(New Vehicle("Mitsubishi"))
And when you loop it,
Dim car as Vehicle
For each car in Vehicles
ls.Items.Add(car.Make)
Next
Hope it helps.

Selecting variable by name

I'm not sure of the appropriate search term so please close and point me to a duplicate if that's the case.
I have a structure with has multiple variables. I've created an array of the type of the structure. I've added data to a few indexes of the array. I now want to select the a specific variable based on the name of the variable.
He's an example of the structure:
structure struc
dim name as string
dim lvl as integer
dim capacity as integer
end structure
And the deceleration of the array:
dim vills(3) as struc
You should be using public fields, not variables Actually you have the terminology correct according to MSDN, the only thing you are missing is the access modifier i.e.
Public Structure Struc
Public Name As String
Public Lvl As Integer
Public Capacity As Integer
End Structure
At the moment your variables are private which means they aren't accessible from outside your Structure.
Looking at your comments it looks like you are trying to access the property by name dynamically rather than knowing it at compile time. There are a few ways of doing this, most of which involve some Reflection.
You should perhaps have a look at ExpandObject - it's effectively a key/value dictionary with the characteristics of a normal class-type object so you get the best of both worlds e.g.
Dim struct As Object = New ExpandoObject()
struct.name = "SomeValue"
struct.lvl = 3
struct.capacity = 100
Console.WriteLine(struct["name"])
Console.WriteLine(struct["lvl"])
As Tony Hopkinson mentioned, it is possible via Reflection:
Public Class Form1
Structure struc
Dim name As String
Dim lvl As Integer
Dim capacity As Integer
End Structure
Dim vills(3) As struc
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i As Integer = 0 To vills.Length - 1
vills(i).lvl = i * 10
Next
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim fi As Reflection.FieldInfo
Dim fieldName As String = "lvl"
For i As Integer = 0 To vills.Length - 1
fi = vills(i).GetType.GetField(fieldName, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Public)
If Not IsNothing(fi) Then
Dim value As Object = fi.GetValue(vills(i))
Debug.Print(i & ": " & value.ToString)
End If
Next
End Sub
End Class
Up to you to decide if it's worth it...
I really think that if this is an important and frequent requirement (find an element by key) then you should think twice and change your array to a Dictionary(Of string, struc) and use something like this
Dim vills = new Dictionary(of String, struc)
Dim s = new struc()
s.name="k1"
s.lvl=1
s.capacity=1
z.Add(s.name, s)
....
struc c = vills("k1")
if(c IsNot Nothing) Then
Console.WriteLine(c.lvl.ToString())
End If
but if you still want to use an array you could search your struc array by name using Linq
Structure struc
Public Dim name as string
Public Dim lvl as integer
Public Dim capacity as integer
End Structure
Sub Main
Dim vills(3) as struc
....
Dim c as struc = wills.Where(Function(x) (x.name="keyName")).Single()
if(c.name IsNot Nothing) then
Console.WriteLine("Found")
End If
End Sub