Skip to main content
1 of 5
RubberDuck
  • 30.9k
  • 6
  • 71
  • 174

There's no such thing as bullet proof code. It's a mythical legend, a unicorn. We can (and should!) certainly try though. The first step to doing that is writing extremely legible and understandable code. You've done a pretty good job of that, but there's some room for improvement.

#Readability ##Use of Whitespace and Comments

It seems like a silly thing to pick on, but it really does matter. Consider this snippet from your code. It does some things right, but it looks messy.

For g = 1 To 10000
    If Not IsEmpty(Cells(4 + g, 4).value) Then
        NumberOfTags = NumberOfTags + 1
    End If
Next g

If Not ThisWorkbook.OPCServer1 Is Nothing Then       'safety
     Exit Sub

End If

Set ThisWorkbook.OPCServer1 = New OPCServer
Call ThisWorkbook.OPCServer1.Connect(Cells(4, 2))                'connect to the OPC Server
Set OPCGroup1 = ThisWorkbook.OPCServer1.OPCGroups.Add(GrpName) 'add the group

'add the 6 items
ReDim MyOPCItems(NumberOfTags)


For i = 1 To NumberOfTags
    On Error GoTo Error_TagNotFound
    If Cells(4 + i, 6) = 1 Then

The indentation is good, but you seem to arbitrarily use new lines. Sometimes no empty line, sometimes one, sometimes two. It makes for hard to read code. Hard to read code is hard to maintain code. Hard to maintain code will have bugs.

The other thing going on here is the "right aligned" comments. Which, as you can see, aren't right aligned anymore the second you copy paste them anywhere else. I'm okay with end of line comments if they're short and helpful, but forget trying to align them. It's a waste of time. If they state the obvious, remove them. If they are more than a word or two, move them to the line before.

For g = 1 To 10000
    If Not IsEmpty(Cells(4 + g, 4).value) Then
        NumberOfTags = NumberOfTags + 1
    End If
Next g

If Not ThisWorkbook.OPCServer1 Is Nothing Then
    Exit Sub
End If

'connect to the OPC Server
Set ThisWorkbook.OPCServer1 = New OPCServer
Call ThisWorkbook.OPCServer1.Connect(Cells(4, 2))   

Set OPCGroup1 = ThisWorkbook.OPCServer1.OPCGroups.Add(GrpName) 

'add the 6 items
ReDim MyOPCItems(NumberOfTags)

For i = 1 To NumberOfTags
    On Error GoTo Error_TagNotFound

##Scoping (kind of)

This is not a scoping issue with your code per se, but still a readability issue.

'Dim OPCServer1 As OPCServer
Dim WithEvents OPCGroup1 As OPCGroup
Dim MyOPCItems() As OPCItem
Dim NumberOfTags As Integer
Dim ReadInterval As Double

Dont' use Dim at the module level. Use Private. Yes, they both do the same thing, but using Private is a visual indication that these variables belong to the module scope.

You should also explicitly declare the scope of your Subs & Functions. Everything is public by default, but I find it much nicer to say so. (Particularly since in VB.Net, everything is private by default. It makes for less confusion when switching between the languages.)

So,

Sub OPC_Connect()

Should be

Public Sub OPC_Connect()

Or

Private Sub OPC_Connect()

Probably the latter. As a rule of thumb, declare everything private until you prove a need to call it from outside the current module or class.

Dim i As Integer
Dim g As Integer
Dim h As Integer

Here you're declaring 3 different counter variables and using them once. Don't do this. It's clutter. Just re-use the i variable as a counter for each loop. The eye will instantly recognize it as a loop counter. Or even better, extract some methods so you don't have to stress about re-using variables. (I'll come back to that in a bit.)

That was also more of a readability issue, but this "scoping" issue is a seriously dangerous thing. I can not think of a single situation where I ever should have legitimately called just plain old Cells.

'fetch the values from the cells
For i = 1 To 6
 Values(i) = Cells(8 + i, 3)
 If Values(i) = "" Then
  Values(i) = 0
 End If
Next i

Cells the way you have used it here is equivalent to calling ActiveSheet.Cells. Almost never should we be working with the active sheet because the user can change it on us while the code is executing. Avoid activate and select by explicitly setting a variable to the range you want to work with.

##Naming

Names should not have underscores in them unless they are an event procedure. The underscore holds this special meaning in VBA. If it's not an event procedure, just don't do it. It's confusing. Particularly when you're making use of WithEvents. (Kudos for that by the way. I don't see many programmers take advantage of that.)

RubberDuck
  • 30.9k
  • 6
  • 71
  • 174