Author Topic: Single dynamic Form for multiple (mostly) identical machines  (Read 4455 times)

bobbh95

  • Newbie
  • *
  • Posts: 22
    • View Profile
Single dynamic Form for multiple (mostly) identical machines
« on: August 01, 2024, 09:40:01 PM »
Howdy all,

It's my first post here. I've lurked for quite some time, but it's time to actually ask a question this time. It has probably been asked before, but I'm just unsure how to go about writing it out.

I work in controls at my facility, and our controls team is quite small. Basically just enough to get 24/7 coverage, plus a manager that is on call as necessary. Our material handling techs (electromechanical) outnumber us roughly 12:1. The overwhelming majority of them are understandably less technically inclined than our controls team, and won't necessarily set up their SCADA software on a daily basis in a manner that helps them find issues in the building. That's not really their fault - their laptops aren't nitrous-equipped and the SCADA isn't exactly lightning quick either. And it's multiple steps to filter all of the messages shown down to what's relevant at any given moment.

I've already started putting together an HMI that covers our main piece of equipment. I don't think I've necessarily gone about the organization of it particularly well, as I'm neither a software engineer nor experienced designing HMIs.

That said, here's the general picture of what we have:

One big sorter with four induction areas - each of which has either three or four induction lines. Each of these induction lines has an (effectively) identical induct. Each has a photoeye (PE) that corresponds to a belt, as well as a PE array. These PEs and their reflectors are a continuous source of faults on the inducts.

The sorter also has a number of other detectors, pullcord e-stops, and various other potential STOP causes.

So far, I have a form (MainForm) that contains a TabControl. There are a few tabs, showing the status of e-stops and other sensors/detectors.

I have set up one of the tabs to show a collection of PilotLights that each show the "faulted" status of an induct. I also have the .Click event for each PilotLight set a Public "called_value" in a NewForm, then NewForm.Show()

This NewForm has a Private "shown_value" which is set to "called_value" so long as "called_value" != Nothing, and a SevenSegment.Value is set to "shown_value" on NewForm.Load()


Here's where I'm coming up short:

I want to have NewForm to have a GroupBox containing RadioButtons indicating which induct is being shown. I also want the SevenSegment to indicate the same. I would like to RadioButtons to update any components or objects in NewForm to point to the appropriate induct if they are changed. I would also like, if NewForm is already shown, the pressing of one of the PilotLights on MainForm to update everything in NewForm.


I think I'll be able to figure out all of the changing of Tag references for all of the indicators I intend to put into NewForm, so long as I get some help with the overall structure/framework/form of the solution.

Would it be best to make a Public Sub UpdateForm() to update the form, and make each OnClicked/.Click() for the RadioButton/PilotLight call UpdateForm()? Is there a more idiomatic way to go about this in VB in general, or in AHMI's framework? Have I gone about constructing my HMI like a fool from the very get-go? Should I go back to digging ditches and pulling wire?

bobbh95

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Single dynamic Form for multiple (mostly) identical machines
« Reply #1 on: August 01, 2024, 09:54:50 PM »
For what it's worth:

I have already made NewForm open from the PilotLights with the appropriate induct number shown in both the SevenSegment as well as the RadioButtons.

The SevenSegment feels fairly idiomatic - I just set the SevenSegment.Value = shown_value on NewForm.Load() and all is well.

My solution for the RadioButtons feels super hacky to me -

I have each RadioButton named rad1, rad2, etc. Each PilotLight.Click() event makes the appropriate rad#.Clicked = True


I understand that this is largely not even an AHMI question at this point, and is more of a VB question, but that's where I'm at. My programming is (in chronological order): HTML/CSS -> a weekend of Java -> I sneezed once when I walked by a C/C++ book at a public library -> oh cool, my calculator has BASIC -> more Java because Minecraft -> Python -> Ladder Logic

Sprinkle in some familiarity with a variety of other languages, except that we hate JavaScript.

I greatly appreciate any and all help that may come my way with this, and I don't/won't blame anyone if I get hit with a few "Read This Fancy Manual"s as we go.

I'll go ahead and make a mockup of what I have on my work computer so far, while I'm not at said computer. Just to help make more clear what exactly I already have, as well as what I would like to make happen.

Edit: I don't appear to be able to make the mockup exactly as intended. Something isn't quite working right at this moment in time, and I can't add a SevenSegment2 to the mockup. I get an error message indicating a "StringMaybeFull" - which I am interpreting as "I have made a path that is too long for Windows to parse," and I'm too tired to refactor this mockup right now. Maybe tomorrow when I am fresh off of work.
« Last Edit: August 01, 2024, 10:07:27 PM by bobbh95 »

bobbh95

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Single dynamic Form for multiple (mostly) identical machines
« Reply #2 on: August 02, 2024, 07:58:33 PM »
Never mind!   :)

I figured it out while fiddling with it throughout the day. It isn't... elegant. But it works and doesn't throw any runtime errors. And that's good enough for me, for the time being.

I used some case switching and went with a Public Sub NewForm.Update_Form() and Me.Update_Form() route, where many OnClicked events set the called_value then call the method. I'll give sanitizing the code a go tomorrow at work and post here.

dmroeder

  • Administrator
  • Full Member
  • *****
  • Posts: 212
    • View Profile
Re: Single dynamic Form for multiple (mostly) identical machines
« Reply #3 on: August 02, 2024, 08:34:07 PM »
Never mind!   :)

I figured it out while fiddling with it throughout the day. It isn't... elegant. But it works and doesn't throw any runtime errors. And that's good enough for me, for the time being.

I used some case switching and went with a Public Sub NewForm.Update_Form() and Me.Update_Form() route, where many OnClicked events set the called_value then call the method. I'll give sanitizing the code a go tomorrow at work and post here.

Nice!  While  you might not consider your solution elegant, chrome don't get ya home!

bobbh95

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Single dynamic Form for multiple (mostly) identical machines
« Reply #4 on: August 02, 2024, 08:56:23 PM »
Can't even attempt to argue with that. I'll need to go back and try some refactoring, cause I ended up repeating myself quite a bit. I may or may not be setting shown_value to called_value three times in a row...  8)

Edit:

Here's a skeleton version of what I came up with, but also with slightly better thought out code. Turns out I was right - the path I had put the solution in on my personal laptop was too long to add the seven segment display haha.


In MainForm.vb
Code: [Select]

    Private Sub Line1_Click(sender As Object, e As EventArgs) Handles line1.Click
        Page2.called_value = 1
        Page2.Update_Form()
    End Sub

    Private Sub Line2_Click(sender As Object, e As EventArgs) Handles line2.Click
        Page2.called_value = 2
        Page2.Update_Form()
    End Sub
...


And in Page2.vb

Code: [Select]
    Public called_value As Integer
    Private shown_value As Integer
    Private pe_list As New List(Of PilotLight)
    Private address_ends As New List(Of String) From {
            "[4].6",
            "[4].7",
            "[5].0"
    }

    Private Sub Collection_Maker()
        pe_list.Add(Me.pe1)
        pe_list.Add(Me.pe2)
        pe_list.Add(Me.pe3)
    End Sub

    Public Sub Update_Form()
        If Me.IsHandleCreated Then
        Else
            Me.Show()
        End If
        shown_value = called_value
        Select Case shown_value
            Case 1
                rad1.Checked = True
                PLC_Comm.ProcessorSlot = 1
            Case 2
                rad2.Checked = True
                PLC_Comm.ProcessorSlot = 1
            Case 3
                rad3.Checked = True
                PLC_Comm.ProcessorSlot = 2
            Case 4
                rad4.Checked = True
                PLC_Comm.ProcessorSlot = 2
        End Select
        SevenSeg.Value = shown_value
        Update_PEs()
    End Sub

    Private Sub Update_PEs()
        Dim index = 0
        For Each pe In pe_list
            pe.PLCAddressValue = "DCP_IO_" & shown_value & ".I.Data" & address_ends.Item(index)
            index += 1
        Next
    End Sub

    Private Sub Debug_Logger()
        Debug.WriteLine("PLC Slot: " & PLC_Comm.ProcessorSlot)
        Debug.WriteLine("PE 1 Address: " & Me.pe1.PLCAddressValue)
        Debug.WriteLine("PE 2 Address: " & Me.pe2.PLCAddressValue)
        Debug.WriteLine("PE 3 Address: " & Me.pe3.PLCAddressValue)
    End Sub

    Private Sub Page2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Collection_Maker()
    End Sub

    Private Sub Rad1_Clicked(sender As Object, e As EventArgs) Handles rad1.Click
        Me.called_value = 1
        Update_Form()
    End Sub
    Private Sub Rad2_Clicked(sender As Object, e As EventArgs) Handles rad2.Click
        Me.called_value = 2
        Update_Form()
    End Sub

...

    Private Sub BasicButton1_Click(sender As Object, e As EventArgs) Handles BasicButton1.Click
        Debug_Logger()
    End Sub
End Class

This seems to work quite well for what I intended.

Another edit: I've come to realize I can remove every "Me.whatever" in there - don't need Me.called_value for this, for example.
« Last Edit: August 02, 2024, 10:34:54 PM by bobbh95 »