AdvancedHMI project example

On second thought, here is my work in progress, the more eyes the better. If you see or know of a better way please let me know. Again, I'm not very good with .NET.

https://drive.google.com/file/d/0B-5kPtgWJjV9REIzdjU1dU45TEk/view?usp=sharing

I can't share the PLC program. But really all you need is an array of strings and DINTs. My alarms are structured, but for testing you don't have to be that fancy. The basic premise is I have triggers in a DINT array and alarm descriptions in a UDT structure, but again for testing you could use a string array (you will have to change some code). On the strings, alarm #1 is string[1], alarm #34 is string[34], etc. The triggers use bit position, I skip 0.

James
 
With the help of Archie I have a much easier way to do the alarming. I will post the code in a day or so. It will make integrating with other PLC code a lot simpler.

James
 
I spent some time on making the code a little better but the way I had it is still the best. I was dynamically adding addresses to a datasubscriber instead of subscribing via code. Archie mentioned that it's probably better to use code, so I tried. However, using code is limited to simple arrays. I have UDT arrays, which will not work. I have finished testing and I'm happy with how it works. The basic idea is I subscribe to a DINT array. These are the alarm triggers. I could subscribe via code on these, but to keep things consistent I don't. I also subscribe to the alarm description, ID, and zone. These are items specific to my application, others may not need them.

Here is the code for subscriptions, I also create an INI file, if necessary:
Code:
Dim Qty As Integer = Math.Ceiling(Alarm_Quantity / Binary_Length)

        For i = 0 To Qty
            Dim PLCItem_1 As New AdvancedHMI.Drivers.PLCAddressItem
            PLCItem_1.PLCAddress = "HMI_Alarm_Array_Z1[" & i & "]"
            Alarm_Active_DataSubscriber.PLCAddressValueItems.Add(PLCItem_1)

        Next

        For i = 1 To Alarm_Quantity

            If CreateFile = True Then
                ini.WriteValue("Alarm " & i & "", 1, "")  'Alarm #
                ini.WriteValue("Alarm " & i & "", 2, "")  'Alarm Description
                ini.WriteValue("Alarm " & i & "", 3, "")  'Alarm Is Active
                ini.WriteValue("Alarm " & i & "", 4, "")  'Alarm Active Time
                ini.WriteValue("Alarm " & i & "", 5, "")  'Alarm Reset Time
                ini.WriteValue("Alarm " & i & "", 6, "")  'Alarm Zone(s)
                CreateFile = False
            End If

            Dim PLCItem_2 As New AdvancedHMI.Drivers.PLCAddressItem
            Dim PLCItem_3 As New AdvancedHMI.Drivers.PLCAddressItem
            Dim PLCItem_4 As New AdvancedHMI.Drivers.PLCAddressItem
            PLCItem_2.PLCAddress = "Alarm[" & i & "].Description_ID"
            PLCItem_3.PLCAddress = "Alarm[" & i & "].ID"
            PLCItem_4.PLCAddress = "Alarm[" & i & "].Zone"
            Alarm_Desc_DataSubscriber.PLCAddressValueItems.Add(PLCItem_2)
            Alarm_Desc_DataSubscriber.PLCAddressValueItems.Add(PLCItem_3)
            Alarm_Desc_DataSubscriber.PLCAddressValueItems.Add(PLCItem_4)

        Next

I keep up with my alarms in an INI file. I do this because it's an easy way to "remember" in case the application is shut down. During subscribe and on data change I write to this file:

Code:
Private Sub Alarm_Desc_DataSubscriber_DataChanged(sender As Object, e As Drivers.Common.PlcComEventArgs) Handles Alarm_Desc_DataSubscriber.DataChanged

        For i = 0 To Alarm_Desc_DataSubscriber.PLCAddressValueItems.Count - 1

            If e.PlcAddress = "Alarm[" & i + 1 & "].Description_ID" Then
                ini.WriteValue("Alarm " & i + 1 & "", 2, e.Values(0)) 'Set Current Alarm Description In INI file
            End If

            If e.PlcAddress = "Alarm[" & i + 1 & "].ID" Then
                ini.WriteValue("Alarm " & i + 1 & "", 1, e.Values(0)) 'Set Current Alarm # In INI File
                If Not Data_Synced Then
                    Data_Syncing += 1
                    Header.Header_Control1.prg_Data_Syncing.Value = Data_Syncing
                    Header.Header_Control1.prg_Data_Syncing.Maximum = Alarm_Quantity
                End If
            End If

            If e.PlcAddress = "Alarm[" & i + 1 & "].Zone" Then
                ini.WriteValue("Alarm " & i + 1 & "", 6, e.Values(0)) 'Set Current Alarm Zone In INI file
            End If

        Next i

        If Data_Syncing >= Alarm_Quantity And Alarm_Quantity > 0 Then
            Data_Synced = True
        End If

        Populate_Active_Alarms_Table()

    End Sub

Trigger data change:
Code:
Private Sub Alarm_Active_DataSubscriber_DataChanged(sender As Object, e As Drivers.Common.PlcComEventArgs) Handles Alarm_Active_DataSubscriber.DataChanged

        '*******************************************************************************
        '* On Data Change Determine Active Alarms, Get Bit Position Then Read INI
        '* File, If Alarm is Active But INI File Doesn't Show This, Then Set Active
        '* In INI File Then Set On Time Stamp
        '* Binary Offset Is The Word [0], [1], [2], etc. /Bit Address
        '*******************************************************************************

        For i = 0 To Alarm_Active_DataSubscriber.PLCAddressValueItems.Count - 1

            Binary_Offset = i * Binary_Length
            Dim dt = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")

            If e.PlcAddress = "HMI_Alarm_Array_Z1[" & i & "]" Then
                For b = 0 To 31
                    If (e.Values(0) And (1 << b)) <> 0 Then
                        Dim result = ini.ReadValue("Alarm " & b + Binary_Offset & "", 3) 'Read Alarm Active State
                        If result <> "1" Then
                            ini.WriteValue("Alarm " & b + Binary_Offset & "", 4, dt) 'If Alarm Active State Is Not 1 Then Set Active Time To Now
                        End If
                        ini.WriteValue("Alarm " & b + Binary_Offset & "", 3, 1) 'Set Alarm Active State To 1
                    Else
                        Dim result = ini.ReadValue("Alarm " & b + Binary_Offset & "", 3) 'Read Alarm Active State
                        If result <> "0" Then
                            ini.WriteValue("Alarm " & b + Binary_Offset & "", 5, dt) 'If Alarm Active State Is Not 0 Then Set Reset Time To Now
                            Dim alarmID = b + Binary_Offset
                            Alarm_History_Data(alarmID)
                        End If
                        ini.WriteValue("Alarm " & b + Binary_Offset & "", 3, 0) 'Set Alarm Active State To 0
                    End If
                Next

            End If

        Next i

        Populate_Active_Alarms_Table()

    End Sub

Load the active table:
Code:
Public Sub Populate_Active_Alarms_Table()

        dt_Active_Alarms = New DataTable
        dt_Active_Alarms.Columns.Add("#", GetType(Integer))
        dt_Active_Alarms.Columns.Add("Name")
        dt_Active_Alarms.Columns.Add("Fault Time")

        If Data_Syncing >= Alarm_Desc_DataSubscriber.PLCAddressValueItems.Count / 3 Then

            For i = 1 To Alarm_Quantity
                Dim Number = ini.ReadValue("Alarm " & i & "", 1)  'Alarm #
                Dim Name = ini.ReadValue("Alarm " & i & "", 2)    'Alarm Description
                Dim Active = ini.ReadValue("Alarm " & i & "", 3)  'Alarm Is Active
                Dim Time = ini.ReadValue("Alarm " & i & "", 4)    'Alarm Active Time
                Dim Zone = ini.ReadValue("Alarm " & i & "", 6)    'Alarm Zone

                If IsNumeric(Zone) Then
                    For bit = 0 To 31
                        If (Zone And 2 ^ bit) > 0 Then
                            If bit = Who_Am_I Then 'If Who Am I & active alarm are the same then add to table
                                Dim dr As DataRow = dt_Active_Alarms.NewRow
                                If Active = "1" Then
                                    dr("#") = Number
                                    dr("Name") = Name
                                    dr("Fault Time") = Time
                                    dt_Active_Alarms.Rows.Add(dr)
                                End If
                            End If
                        End If
                    Next
                End If

            Next

            Alarm_Active.Populate_DGV()

        End If

    End Sub

Load the history table:
Code:
Private Sub Alarm_History_Data(ByVal alarmID As Integer)

        Dim alarmText = ini.ReadValue("Alarm " & alarmID & "", 2)
        Dim alarmActiveTime = ini.ReadValue("Alarm " & alarmID & "", 4)
        Dim dt = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")

        If System.IO.File.Exists(AlarmHistoryPath) = True Then

            Using sw As New System.IO.StreamWriter(AlarmHistoryPath, True)
                If alarmActiveTime <> "" Then
                    sw.WriteLine(alarmID & "," & alarmText & "," & alarmActiveTime & "," & dt)
                    sw.Close()
                End If
            End Using

        Else
            File.Create(AlarmHistoryPath).Dispose()

            Using sw As New System.IO.StreamWriter(AlarmHistoryPath, True)
                If alarmActiveTime <> "" Then
                    sw.WriteLine(alarmID & "," & alarmText & "," & alarmActiveTime & "," & dt)
                    sw.Close()
                End If
            End Using

        End If

        Alarm_History_Table()

    End Sub

This is just a brief explanation, if interested I suggest going through the app I linked in a previous post. If you have questions, I will try my best to answer them.

What's nice about this setup is that the description changes which allows me to use one alarm trigger for multiple alarms. Example, we have several addon instructions, one being a Powerflex 525. When the drive faults or I get a general alarm, I simply activate the alarm and then pass the actual description to the alarm handler (which then goes to the HMI). I might have 30-40 different reasons for the alarm, but I only have to use one alarm. If I explained that poorly I apologize.

James
 
I posted a sample app on AHMI forum this morning that showcases alarming. It is currently setup for Modbus comms. I included a sample PLC program, AD productivity 2000.

Here's the forum link:
http://advancedhmi.com/forum/index.php?topic=1813.msg10341#msg10341

James

For those following along at home, you will need Microsoft's VisualBasic PowerPacks to build this solution.

Here's Archie's post on how to enable it:

http://advancedhmi.com/forum/index.php?topic=272.0


Microsoft still points you to v3.0 if you do a web search, we apparently want v10.

Where to find it:

https://social.msdn.microsoft.com/F...sual-basic-power-packs-100?forum=vbpowerpacks


Hmmm, installing v10 made the tab show "True" while the Property window (where you change it) would reset itself back to false whenever I tried to change it. Removing v10 and installing v3 (which reports itself internally as v9) fixed this.

I had to add "Imports System.Data" to Users.vb, Report_frm.vb, _0000_Main_Parent.vb, and _0101_Report.vb to clear up to clear up the "Type 'DataTable' not defined error.

And it looks like I need to install ReportViewer. Which I tried, and it doesn't seem to be the correct version. Gotta love it...
 
Last edited:
For those following along at home, you will need Microsoft's VisualBasic PowerPacks to build this solution.

Here's Archie's post on how to enable it:

http://advancedhmi.com/forum/index.php?topic=272.0


Microsoft still points you to v3.0 if you do a web search, we apparently want v10.

Where to find it:

https://social.msdn.microsoft.com/F...sual-basic-power-packs-100?forum=vbpowerpacks


Hmmm, installing v10 made the tab show "True" while the Property window (where you change it) would reset itself back to false whenever I tried to change it. Removing v10 and installing v3 (which reports itself internally as v9) fixed this.

I had to add "Imports System.Data" to Users.vb, Report_frm.vb, _0000_Main_Parent.vb, and _0101_Report.vb to clear up to clear up the "Type 'DataTable' not defined error.

And it looks like I need to install ReportViewer. Which I tried, and it doesn't seem to be the correct version. Gotta love it...

Thank you for pointing that stuff out. None of that is needed (reports or power packs), I forgot to remove. I will post another project in the morning and hopefully that will clear up those issues.

James
 
No problem, I was curious and wanted to check it out (which I will when I get into work). Thanks!
 

Similar Topics

Here is our latest post on deploying an AdvancedHMI project on the industrial floor. Here are a few thing we cover: Hardware - Computer - USP -...
Replies
2
Views
2,502
I would like to temporarily install AdvancedHMI on the same computer as FactoryTalk View SE. I am waiting for another contractor to finish the FTV...
Replies
3
Views
550
Hi all, just wanna ask if anyone has used an M221 controller and successfully connected to AdvancedHMI
Replies
1
Views
1,308
MessageDisplayByValue question is there a way to embed a tag value in the messages of the MessageDisplayByValue function ??? Similar to what is...
Replies
10
Views
2,811
Hi All I have been switching between 2017 and 2019, all depending on which one i found more stable, at the moment I use 2019 I was just...
Replies
1
Views
1,473
Back
Top Bottom