Results 1 to 9 of 9

Thread: Updating 512 Values Over Varrying Times

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Question Updating 512 Values Over Varrying Times

    Hey All,

    I've got, i guess more of a best practice issue to do with updating 512+ values at once at different speeds.
    I'll give a basic run down of what i'm trying to do.

    Essentially i'm connecting to lights over DMX. For those who are unformilliar. DMX can carry 512 addresses over 1 universe (a universe being essentially 1 XLR cable). A light can take anywhere from 1 to 512 addresses. A simple light might take 1 address just for intensity. Another may have 1 for intensity, 3 for colour (RGB), 2 for pan and tilt.

    So sending an array of 512 values and then sending another update to a new set of values is easy enough.

    However, this would create a snap. More often then not we want the lights to get to their final value over a time. Then those addresses will all have a different distance to travel in that time. If that wasn't hard enough, those addresses may have different times they are updating over. AND may have a delay on that cue that the light waits for before starting it's travel.

    So currently my thought is 512 tasks, 2 timers/loops each, each tick or loop calculating where the address should be up to, then applying that to the array.
    However, i'm fairly sure starting that many threads/tasks would be a crime. Also assuming that would cause alot of issues of intanglement, trying to access the same array.

    The next issue with that is i mentioned 512 is 1 universe. Because of how many addresses modern lights need, multiple universes have become required. And while each universe would be it's own array, it would be another 512 timers going.

    I would also note that while this has all been linear, i plan to add curves, so i would need a chance to do a calculation on each 'tick'

    Final note, a cost saving method would be to only spin up calculations for the addresses that changed on a cue, but a cue could still require all of those addresses, and would be an extra calculation before the lights changed.


    So that is my conundrum, essentially just after some best practice advice for something on this scale.


    Thanks!

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Updating 512 Values Over Varrying Times

    It wouldn't necessarily be a crime, but it certainly MIGHT be. The first question that comes to mind is: How many will be changing at any one time, and how fast will they be changing?

    The forms timer can have a tick resolution down around 50ms, and other timers can have somewhat better resolution. However, people can't really perceive changes that fast, so you might be thinking more along the lines of 100ms intervals, which would mean 10 'events' per second, and a nice round figure to work with.

    What I'm thinking of may not apply depending on how you answer that first question, but could you use one timer? A timer ticking at 100ms intervals could act as a 'heartbeat'. Any change that could be a multiple of 100ms could then be handled by that tick. Things that had to change every 100ms would take action every beat, things that had to change every 200ms would take action every other beat, and so forth. If that series of intervals was good enough, then you could create a class that held the information that you needed, and had a method called something like TakeAction. If you had a list of those classes, then any time the timer ticked, it would just iterate through the list calling TakeAction. Within that method, depending on the settings for the class, either it would send something, or it would increment a counter. The counter would be counting the beats, with the expectation that every N beats it would send something.

    Such an approach has a couple obvious points of failure. For one thing, if the interval has to be greater than multiples of 100ms, it could get terribly problematic. After all, to use one timer, any action taken in the tick event has to be fully completed before the next tick occurs, or that next tick will be skipped or delayed. If the interval is too short, or too much has to happen in one tick, you'd end up with very erratic behavior. Therefore, if multiples of 100ms is too coarse, or there are too many signals being sent, or sending one signal is too slow, then using a single timer simply won't work. In that case, you have no real choice aside from, at the very least, using threading in some fashion.

    Even in the case of threading, though, you will end up with erratic behavior if there are enough signals being sent. After all, when you have threading, you can only run as many simultaneous processes as you have CPU cores to work with. Furthermore, threads can have a slight overhead to them, which will get much worse if a single core has multiple threads swapping in and out. You get around a LOT of that because of the timers. Having a timer means that your thread will spend a potentially significant amount of time sitting idle, which is really ideal when threading, since the CPU is free to work on other things.

    Overall, as long as you have enough processes, it's an interesting problem in logistics. What I would favor is a single timer, but spawning Tasks to do the communication. Reducing the number of timers will reduce the incidence of threads stepping on one another, but the actual communication might all take place in Tasks.
    My usual boring signature: Nothing

  3. #3

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Re: Updating 512 Values Over Varrying Times

    Thanks Shaggy for that.

    Just to clarify a few things, this would just be updating a table. The device i'm starting with support for has a driver i hook into, and it then sends the data itself. It will also resend it about every 23ms by default. However not all will act this way so i'm just keeping to the list to keep options open.

    As for how tight the timings need to be, 10ms is the lowest value we talk in apart from instant. While both are instant to the eyes, if we are doing a repeating effect that syncs up with something else, 10ms adds up over time. So precision is key.
    And it should be able to get through all 512 addresses. And then i'd duplicate the system for the next universe

    I've taken on the heartbeat method, and created a little test run. Essentially it runs a syncronous test and then an asyncronous test. That way i could tell what was quicker.

    I have 3 different tests, although you have to switch the 2 asyncs over manually before building.
    The test will asign a final value and a run time to each of the addresses, then run the 2 tests. It will then spit out the times of the test, in order of:
    Average update time
    Max Update Time
    Elapsed Time
    Updates required to finish


    1st test runs fine, i've actually got to adjust it's curve as it is finishing too early in about 300ms.
    the Parrarel.For test finishes fairly slow at the 8000ms mark (unless i'm doing something wrong here i'm assuming this is just not a fast way of doing it)
    And the task async test finishes last at a few minutes. However it updates the values the quickest.
    The loop will continue until all values equal their final, however the Task version keeps overwriting itself. There is even a ghost tick of the timer where even though i set the for loop to go from 0-511 it would hit 512 causing a crash.

    I'm not sure how it overruns itself in that part, i stop the clock for every tick so that it couldn't, then makeup for if the loop was slow by calculating from time when the cue started. And I await for all tasks to complete before initiating another update.

    I've put the link below (wouldnt let me attach directly), if you have any pointers on where it might be going wrong that would be great. I've disabled all the DMX sending stuff so it should work fine
    Last edited by Shaggy Hiker; Jun 6th, 2022 at 10:18 AM.

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Updating 512 Values Over Varrying Times

    I had to remove the link due to a rule we have. Normally, I'd ask you to remove the bin and obj folders and re-post the link, as we don't allow compiled code to be linked to in this part of the forum (we've been burned in the past, and it was quite entertaining), however, in this case, is there a lot of code for the tests? This seems like the kind of question that there are a fair number of people on here who would have interesting contributions to it. If the code for the tests isn't too bad, perhaps you could post just that (wrapped in CODE tags)? You'll certainly get better input from that. After all, that would become the kind of question folks can really geek out on.
    My usual boring signature: Nothing

  5. #5
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: Updating 512 Values Over Varrying Times

    Am I correct:

    - DMX defines a set of timeslots with each device taking a certain number of slots.
    - the slot(s) are the address of the device
    - periodically all bytes for a controller are sent
    - the max slots for a device or device port is 512
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Re: Updating 512 Values Over Varrying Times

    Ahh all good, sorry about that ahaha.

    I've fixed the issue where it was telling me it completed early, fumbled a line.
    So the current finish times are:
    Sync: 1200-1300ms
    Parallel.For: 8000-8300ms
    Task: DNF

    Here is the code:

    Code:
    Imports System.Threading
    
    Public Class MainForm
    
        Private Declare Function isDMXOpen Lib "HD512DLL.dll" () As Boolean 'Please Change your DLL DIR
        Private Declare Function ShowAbout Lib "HD512DLL.dll" () As Boolean
        Private Declare Function DMXClose Lib "HD512DLL.dll" () As Short
        Private Declare Function DMXSend Lib "HD512DLL.dll" (ByVal Channel1 As Integer, ByVal Value1 As Byte) As Boolean
        Private Declare Function DMXSends Lib "HD512DLL.dll" (ByVal ChannelCount As Integer, ByVal ChannelIndex As Integer, ByRef Buffer As Byte) As Boolean
    
        Dim MyData As Byte
        Dim m_DMXData(512) As Byte
    
        Dim NewValuesList As New List(Of List(Of Integer))
    
        Private CueClock As New Timers.Timer(10)
    
        Private DMXClock As New Timers.Timer(10)
    
        Private CueStartTime As New DateTime
    
        Private AsyncTimerList As New List(Of Integer)
    
        Private Mode As String
    
        Private SyncTimerList As New List(Of Integer)
    
        Private StopWatch As New Stopwatch
    
        Private DMXLock As New Object
    
        Private SyncRunTime As Integer
    
        Private AsyncRunTime As Integer
    
        Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles Me.Load
    
            AddHandler CueClock.Elapsed, AddressOf CueClock_Tick
    
            AddHandler DMXClock.Elapsed, AddressOf DMXClock_Tick
    
            For CounterVarA = 0 To 511
    
                Dim Item As New ListViewItem()
    
                Item.Text = CounterVarA
    
                Item.SubItems.Add(0)
    
                DMXListView.Items.Add(Item)
    
            Next
    
        End Sub
    
        Private Property DMXData(ByVal ChannelIndex As Integer) As Byte
    
            Get
    
                SyncLock DMXLock
    
                    Return m_DMXData(ChannelIndex)
    
                End SyncLock
    
            End Get
            Set(value As Byte)
    
                SyncLock DMXLock
    
                    m_DMXData(ChannelIndex) = value
    
                End SyncLock
    
            End Set
    
        End Property
    
        Private Sub TestButton_Click(sender As Object, e As EventArgs) Handles TestButton.Click
    
            StatusLabel.Text = "Warming Up"
    
            AddressLTimeLabel.Text = "0.0ms"
    
            SynchronousAvgLabel.Text = "0.0ms"
    
            SynchronousHLabel.Text = "0.0ms"
    
            SynchronousELabel.Text = "0.0ms"
    
            SynchronousRLabel.Text = "0"
    
            AsynchronousAvgLabel.Text = "0.0ms"
    
            AsynchronousHLabel.Text = "0.0ms"
    
            AsynchronousELabel.Text = "0.0ms"
    
            AsynchronousRLabel.Text = "0"
    
            SyncRunTime = 0
    
            AsyncRunTime = 0
    
            AsyncTimerList.Clear()
    
            SyncTimerList.Clear()
    
            For CounterVarR As Short = 0 To 511
    
                DMXData(CounterVarR) = 0
    
            Next CounterVarR
    
            DMXSends(512, 1, DMXData(0))
    
            For CounterVar As Short = 0 To 511
    
                Dim NewAddressValues As New List(Of Integer)
    
                NewAddressValues.Add(DMXData(CounterVar))
    
                NewAddressValues.Add(CInt(Math.Ceiling(Rnd() * 255)) + 0)
    
                NewAddressValues.Add(1000) 'CInt(Math.Ceiling(Rnd() * 1000)) + 0)
    
                NewValuesList.Add(NewAddressValues)
    
            Next CounterVar
    
            Mode = "Sync"
    
            CueStartTime = DateTime.Now
    
            CueClock.Start()
    
            StatusLabel.Text = "Running Syncronous"
    
            TestButton.Enabled = False
    
            DMXClock.Start()
    
        End Sub
    
        Private Async Sub CueClock_Tick(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
    
            CueClock.Stop()
    
            StopWatch.Start()
    
            Dim ValueChanged As Boolean = False
    
            If Mode = "Sync" Then
    
                For CounterVarS = 0 To 511
    
                    Dim NewValues As List(Of Integer) = NewValuesList(CounterVarS)
    
                    If NewValues(1) <> DMXData(CounterVarS) Then
    
                        ValueChanged = True
    
                        CalculateDMXLevel(CounterVarS, (e.SignalTime.Ticks - CueStartTime.Ticks) / 10000, NewValuesList(CounterVarS))
    
                    End If
    
                Next CounterVarS
    
                SyncTimerList.Add(StopWatch.ElapsedMilliseconds)
    
            ElseIf Mode = "Async1" Then
    
                Parallel.For(0, 512, Sub(CounterVarS)
                                         For CounterVarS = 0 To 511
    
                                             Dim NewValues As List(Of Integer) = NewValuesList(CounterVarS)
    
                                             If NewValues(1) <> DMXData(CounterVarS) Then
    
                                                 ValueChanged = True
    
                                                 CalculateDMXLevel(CounterVarS, (e.SignalTime.Ticks - CueStartTime.Ticks) / 10000, NewValuesList(CounterVarS))
    
                                             End If
    
                                         Next CounterVarS
                                     End Sub)
    
                AsyncTimerList.Add(StopWatch.ElapsedMilliseconds)
    
            ElseIf Mode = "Async2" Then
    
                Dim TaskList As New List(Of Task)
    
                For CounterVarA As Short = 0 To 511
    
                    Dim NewValues As List(Of Integer) = NewValuesList(CounterVarA)
    
                    If NewValues(1) <> DMXData(CounterVarA) AndAlso CounterVarA < 511 Then
    
                        Dim UpdateTask As New Task(Sub() CalculateDMXLevel(CounterVarA, (e.SignalTime.Ticks - CueStartTime.Ticks) / 10000, NewValuesList(CounterVarA)))
    
                        TaskList.Add(UpdateTask)
    
                        UpdateTask.Start()
    
                        ValueChanged = True
    
                        'Me.BeginInvoke(Sub() CalculateDMXLevel(CounterVarA, (e.SignalTime.Ticks - CueStartTime.Ticks) / 10000, NewValuesList(CounterVarA)))
    
                    End If
    
                Next CounterVarA
    
                Await Task.WhenAll(TaskList)
    
                AsyncTimerList.Add(StopWatch.ElapsedMilliseconds)
    
            End If
    
            StopWatch.Reset()
    
            If ValueChanged = False AndAlso Mode = "Sync" Then
    
                Mode = "Async2" 'Change me to swap Async test
    
                Me.BeginInvoke(Sub() StatusLabel.Text = "Running Asyncronous")
    
                For CounterVarR As Short = 0 To 511
    
                    DMXData(CounterVarR) = 0
    
                Next CounterVarR
    
                SyncRunTime = (DateTime.Now.Ticks - CueStartTime.Ticks) / 10000
    
                CueStartTime = DateTime.Now
    
                CueClock.Start()
    
            ElseIf ValueChanged = False Then
    
                AsyncRunTime = (DateTime.Now.Ticks - CueStartTime.Ticks) / 10000
    
                Me.BeginInvoke(Sub() FinishCue())
    
            Else
    
                CueClock.Start()
    
            End If
    
        End Sub
    
        Private Async Function CalculateDMXLevel(ByVal Channel As Integer, ByVal CurrentTime As Integer, ByVal ValueList As List(Of Integer)) As Task
    
            Dim ValueDistance As Integer = ValueList(1) - ValueList(0)
    
            Dim TimePercent As Single = (CurrentTime / ValueList(2))
    
            If TimePercent > 1 Then TimePercent = 1
    
            Me.BeginInvoke(Sub() DMXData(Channel) = (ValueList(1) - ValueList(0)) * TimePercent)
    
        End Function
    
        Private Sub DMXClock_Tick(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
    
            For CounterVarD As Short = 0 To 511
    
                Dim NewValues As List(Of Integer) = NewValuesList(CounterVarD)
    
                If NewValues(1) <> DMXData(CounterVarD) Then
    
                    Dim Item As ListViewItem
    
                    Me.Invoke(Sub() Item = DMXListView.Items(CounterVarD))
    
                    Me.BeginInvoke(Sub() Item.SubItems(1).Text = DMXData(CounterVarD))
    
                    DMXSend(CounterVarD + 1, DMXData(CounterVarD))
    
                End If
    
            Next
    
        End Sub
    
        Private Sub FinishCue()
    
            DMXClock.Stop()
    
            Dim LongestChange As Integer
    
            For Each Address In NewValuesList
    
                If Address(2) > LongestChange Then LongestChange = Address(2)
    
            Next
    
            AddressLTimeLabel.Text = LongestChange & "ms"
    
            If SyncTimerList.Count > 0 Then
    
                SynchronousAvgLabel.Text = SyncTimerList.Average & "ms"
    
                SynchronousHLabel.Text = SyncTimerList.Max & "ms"
    
                SynchronousELabel.Text = SyncRunTime & "ms"
    
                SynchronousRLabel.Text = SyncTimerList.Count
    
            End If
    
            If AsyncTimerList.Count > 0 Then
    
                AsynchronousAvgLabel.Text = AsyncTimerList.Average & "ms"
    
                AsynchronousHLabel.Text = AsyncTimerList.Max & "ms"
    
                AsynchronousELabel.Text = AsyncRunTime & "ms"
    
                AsynchronousRLabel.Text = AsyncTimerList.Count
    
            End If
    
            StatusLabel.Text = "Idle"
    
            TestButton.Enabled = True
    
        End Sub
    
    End Class

  7. #7

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Re: Updating 512 Values Over Varrying Times

    Quote Originally Posted by dbasnett View Post
    Am I correct:

    - DMX defines a set of timeslots with each device taking a certain number of slots.
    - the slot(s) are the address of the device
    - periodically all bytes for a controller are sent
    - the max slots for a device or device port is 512
    Hey Dbasnett, Kind of, i'll just reclarify incase.
    It is similiar to MIDI in some ways of thinking.

    So
    -DMX is the protocal which has 512 Addresses (these are unrelated to time)
    - Yes, each device/light will listen to it's assigned addresses, DMX itself has no idea what is listening though
    - Yes, every 23ms or so, change or no change these are resent. This is the protocals 'Alive' check. However the timer i'm dealing with is seperate to this, as the driver won't allow me to hook into that frame as an auto check.
    - Yes, each port can only carry 512 addresses. Then we move to what we call Universes. which i've seen upwards of 8 universes used on bigger events. However just starting with 2 working universes right now.

  8. #8

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Re: Updating 512 Values Over Varrying Times

    I believe i have found one of the issues with the Task version.

    I was using
    Code:
    Await Task.WhenAll(TaskList)
    Instead of
    Code:
    Task.WaitAll(TaskList.ToArray)
    The former doesn't appear to wait like i thought it did, causing part of the running over i was experiencing.

    Also found i had a double loop in the Parrarel.For causing it to do everything again to the power of 512... whoops.
    It now can finish 100ms quicker than sequential, but run to run they seem to trade blows with about 100-200ms difference. I'm assuming it gets out of the gate quicker when all 512 addresses are moving, but as they hit their finish mark, and we get down to a few addresses updating, the overhead you mentioned Shaggy, comes to the forefront and the sequential claws it's way back

    This:

    Code:
    Parallel.For(0, 512, Sub(CounterVarS)
                                         For CounterVarS = 0 To 511
    
                                            'Stuff 
    
                                         Next CounterVarS
                                     End Sub)
    Becomes:

    Code:
    Parallel.For(0, 512, Sub(CounterVarS)
                                        
                                             'Stuff
                                         
                                     End Sub)
    Last edited by Bensley196; Jun 6th, 2022 at 11:25 PM.

  9. #9

    Thread Starter
    Junior Member
    Join Date
    Jul 2019
    Posts
    21

    Re: Updating 512 Values Over Varrying Times

    So i've tickered all day (I'm Australian BTW hence the weird times), i'm finding that Syncronous, Task, and Pararell.For are all trading blows run to run. I made a threading.thread version. Assuming i've done it correctly it is very slow.

    I didn't find what was cuasing the issue with the Task.run version, but I realised that while the task was passing information to the function, the loop had enough time to go around and start updating the values it was pulling from. SyncLocks weren't working, i believe this was due to it being the same thread (loop thread) accessing the area.
    So i bundled my values and that seems to have worked.
    Like this:
    Code:
    Dim Parameters As Object = New Object() {CounterVarA, CurrentTimems, NewValuesList(CounterVarA)}
    
    TaskList.Add(Task.Factory.StartNew(Sub() CalculateDMXLevel(Parameters)))
    I've posted the code i'm now using below. I have a certain amount of knowledge of threading, but part of me feels like the numbers are too close between the two types that it may not be running async?
    I also have the weird issue of the two windows pc's i have both having 16 cores 32 threads, so i'm assuming the linear runs will start to out pace the threading on a more reasonable pc?

    Assuming i'm representing each test correctly. I'm finding that the first run of both is slower then the rest, especially on the Async version and tend to miss the deadline. I'm not quite sure what is wrong with that. Maybe something is hitting harder the first run? or the CPU is prioritising those threads differenlty the second time

    I'm also finding the timer's inaccuracy hard to compete with. doing a constant loop is very rescource draining but the moment i sleep or use a timer inaccuracies begin to rise. Is there any more accurate timers out there? I was reading up on MultimediaTimer, it does seem a little abandoned? but if not I was thinking maybe using it as a global clock for the 'cues'. It also looks like it is Win32 and my program is 64bit, which depending on its implementation could be an issue.


    Code:
    Imports System.Threading
    
    Public Class MainForm
    
        Private Declare Function isDMXOpen Lib "HD512DLL.dll" () As Boolean 'Please Change your DLL DIR
        Private Declare Function ShowAbout Lib "HD512DLL.dll" () As Boolean
        Private Declare Function DMXClose Lib "HD512DLL.dll" () As Short
        Private Declare Function DMXSend Lib "HD512DLL.dll" (ByVal Channel1 As Integer, ByVal Value1 As Byte) As Boolean
        Private Declare Function DMXSends Lib "HD512DLL.dll" (ByVal ChannelCount As Integer, ByVal ChannelIndex As Integer, ByRef Buffer As Byte) As Boolean
    
        Private m_DMXData(512) As Byte
    
        Private StartValuesList As New List(Of Integer)
    
        Private NewValuesList As New List(Of List(Of Integer))
    
        Private CueClock As New Timers.Timer(10)
    
        Private DMXClock As New Timers.Timer(10)
    
        Private CueStartTime As Double
    
        Private AsyncTimerList As New List(Of Integer)
    
        Private Mode As String
    
        Private SyncTimerList As New List(Of Integer)
    
        Private StopWatch As New Stopwatch
    
        Private DMXLock As New Object
    
        Private SyncRunTime As Integer
    
        Private AsyncRunTime As Integer
    
        Private SyncMode As Integer
    
        Private Complete As Boolean
    
        Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles Me.Load
    
            AddHandler CueClock.Elapsed, AddressOf CueClock_Tick
    
            AddHandler DMXClock.Elapsed, AddressOf DMXClock_Tick
    
            For CounterVarA = 0 To 511
    
                Dim Item As New ListViewItem()
    
                Item.Text = CounterVarA
    
                Item.SubItems.Add(0)
    
                DMXListView.Items.Add(Item)
    
            Next
    
            DMXSends(512, 1, DMXData(0))
    
        End Sub
    
        Private Property DMXData(ByVal ChannelIndex As Integer) As Byte
    
            Get
    
                SyncLock DMXLock
    
                    Return m_DMXData(ChannelIndex)
    
                End SyncLock
    
            End Get
            Set(value As Byte)
    
                SyncLock DMXLock
    
                    m_DMXData(ChannelIndex) = value
    
                End SyncLock
    
            End Set
    
        End Property
    
        Private Sub TestButton_Click(sender As Object, e As EventArgs) Handles TestButton.Click
    
            StatusLabel.Text = "Warming Up"
    
            AddressLTimeLabel.Text = "0.0ms"
    
            SynchronousAvgLabel.Text = "0.0ms"
    
            SynchronousHLabel.Text = "0.0ms"
    
            SynchronousELabel.Text = "0.0ms"
    
            SynchronousRLabel.Text = "0"
    
            AsynchronousAvgLabel.Text = "0.0ms"
    
            AsynchronousHLabel.Text = "0.0ms"
    
            AsynchronousELabel.Text = "0.0ms"
    
            AsynchronousRLabel.Text = "0"
    
            SyncRunTime = 0
    
            AsyncRunTime = 0
    
            AsyncTimerList.Clear()
    
            SyncTimerList.Clear()
    
            NewValuesList.Clear()
    
            Complete = False
    
            For CounterVarR As Short = 0 To 511
    
                StartValuesList.Add(CInt(Math.Ceiling(Rnd() * 255)) + 0)
    
                DMXData(CounterVarR) = StartValuesList.Item(CounterVarR)
    
            Next CounterVarR
    
            DMXSends(512, 1, DMXData(0))
    
            DMXListUpdater()
    
            For CounterVar As Short = 0 To 511
    
                Dim NewAddressValues As New List(Of Integer)
    
                NewAddressValues.Add(DMXData(CounterVar))
    
                NewAddressValues.Add(CInt(Math.Ceiling(Rnd() * 255)) + 0)
    
                NewAddressValues.Add(CInt(Math.Ceiling(Rnd() * 10)) + 0)
    
                NewAddressValues.Add(CInt(Math.Ceiling(Rnd() * (10 - NewAddressValues(2)))) + 0)
    
                NewValuesList.Add(NewAddressValues)
    
            Next CounterVar
    
            Mode = "Sync"
    
            CueStartTime = Math.Floor(Now.Ticks / 10000)
    
            SyncMode = AsyncNumericUpDown.Value
    
            If TimerRadioButton.Checked = True Then
    
                CueClock.Start()
    
            Else
    
                Dim LoopTask As New Task(AddressOf LoopTrack)
    
                LoopTask.Start()
    
            End If
    
            StatusLabel.Text = "Running Syncronous"
    
            TestButton.Enabled = False
    
            DMXClock.Start()
    
        End Sub
    
        Private Function LoopTrack()
    
            While Complete = False
    
                Dim FrameStartms As Double = Math.Ceiling(Now.Ticks / 10000)
    
                DMXUpdate(FrameStartms)
    
                Dim FrameEndms As Double = Math.Ceiling(Now.Ticks / 10000)
    
                If FrameEndms - FrameStartms < 10 Then
    
                    'Thread.Sleep(10 - (FrameEndms - FrameStartms))
    
                End If
    
            End While
    
        End Function
    
    
        Private Sub CueClock_Tick(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
    
            CueClock.Stop()
    
            DMXUpdate(Math.Ceiling(e.SignalTime.Ticks / 10000))
    
            If Complete = False Then
    
                CueClock.Start()
    
            End If
    
        End Sub
    
        Private Sub DMXUpdate(ByVal EventTimems As Double)
    
            StopWatch.Start()
    
            Dim ValueChanged As Boolean = False
    
            Dim PreCurrentTimems As Integer = EventTimems - CueStartTime
    
            If Mode = "Sync" Then
    
                For CounterVarS = 0 To 511
    
                    Dim NewValues As List(Of Integer) = NewValuesList(CounterVarS)
    
                    If NewValues(1) <> DMXData(CounterVarS) Then
    
                        Dim CurrentTimems As Integer = PreCurrentTimems + StopWatch.ElapsedMilliseconds
    
                        ValueChanged = True
    
                        If NewValues(2) <= CurrentTimems Then
    
                            CalculateDMXLevel(CounterVarS, EventTimems - CueStartTime, NewValuesList(CounterVarS))
    
                        End If
    
                    End If
    
                Next CounterVarS
    
                SyncTimerList.Add(StopWatch.ElapsedTicks / 10000)
    
            ElseIf Mode = "Async1" Then
    
                Parallel.For(0, 512, Sub(CounterVarA)
    
                                         Dim NewValues As List(Of Integer) = NewValuesList(CounterVarA)
    
                                         If NewValues(1) <> DMXData(CounterVarA) Then
    
                                             Dim CurrentTimems As Integer = PreCurrentTimems + StopWatch.ElapsedMilliseconds
    
                                             ValueChanged = True
    
                                             If NewValues(2) <= CurrentTimems Then
    
                                                 CalculateDMXLevel(CounterVarA, CurrentTimems, NewValuesList(CounterVarA))
    
                                             End If
    
                                         End If
    
                                     End Sub)
    
                AsyncTimerList.Add(StopWatch.ElapsedTicks / 10000)
    
            ElseIf Mode = "Async2" Then
    
                Dim TaskList As New List(Of Task)()
    
                For CounterVarA As Short = 0 To 511
    
                    Dim NewValues As List(Of Integer) = NewValuesList(CounterVarA)
    
                    If NewValues(1) <> DMXData(CounterVarA) Then
    
                        Dim CurrentTimems As Integer = PreCurrentTimems + StopWatch.ElapsedMilliseconds
    
                        ValueChanged = True
    
                        If NewValues(2) <= CurrentTimems Then
    
                            Dim Parameters As Object = New Object() {CounterVarA, CurrentTimems, NewValuesList(CounterVarA)}
    
                            TaskList.Add(Task.Factory.StartNew(Sub() CalculateDMXLevel(Parameters)))
    
                        End If
    
                    End If
    
                Next CounterVarA
    
                Task.WaitAll(TaskList.ToArray)
    
                AsyncTimerList.Add(StopWatch.ElapsedTicks / 10000)
    
            ElseIf Mode = "Async3" Then
    
                Dim TaskList As New List(Of Thread)()
    
                For CounterVarA As Short = 0 To 511
    
                    Dim NewValues As List(Of Integer) = NewValuesList(CounterVarA)
    
                    If NewValues(1) <> DMXData(CounterVarA) Then
    
                        Dim CurrentTimems As Integer = PreCurrentTimems + StopWatch.ElapsedMilliseconds
    
                        ValueChanged = True
    
                        If NewValues(2) <= CurrentTimems Then
    
                            Dim UpdateThread As New Thread(AddressOf CalculateDMXLevel)
    
                            Dim Parameters As Object = New Object() {CounterVarA, CurrentTimems, NewValuesList(CounterVarA)}
    
                            TaskList.Add(UpdateThread)
    
                            UpdateThread.Start(Parameters)
    
                        End If
    
                    End If
    
                Next CounterVarA
    
                Dim Threading As Boolean = True
    
                While Threading = True
    
                    Threading = False
    
                    For Each Task In TaskList
    
                        If Task.IsAlive Then
    
                            Threading = True
    
                            Task.Join()
    
                        End If
    
                    Next
    
                End While
    
                AsyncTimerList.Add(StopWatch.ElapsedTicks / 10000)
    
            End If
    
            StopWatch.Reset()
    
            If ValueChanged = False AndAlso Mode = "Sync" Then
    
                Mode = "Async" & SyncMode
    
                Me.Invoke(Sub() StatusLabel.Text = "Running Asyncronous")
    
                For CounterVarR As Short = 0 To 511
    
                    DMXData(CounterVarR) = StartValuesList.Item(CounterVarR)
    
                Next CounterVarR
    
                SyncRunTime = (Now.Ticks / 10000) - CueStartTime
    
                CueStartTime = Math.Floor(Now.Ticks / 10000)
    
            ElseIf ValueChanged = False Then
    
                AsyncRunTime = (Now.Ticks / 10000) - CueStartTime
    
                Me.BeginInvoke(Sub() FinishCue())
    
                Complete = True
    
            End If
    
        End Sub
    
        Private Function CalculateDMXLevel(ByVal Parameters As Object)
    
            Dim Channel As Integer = Parameters(0)
    
            Dim CurrentTime As Integer = Parameters(1)
    
            Dim ValueList As List(Of Integer) = Parameters(2)
    
            Dim ValueDistance As Integer = ValueList(1) - ValueList(0)
    
            Dim TimePercent As Single = (CurrentTime - ValueList(2)) / ValueList(3)
    
            If TimePercent > 1 OrElse ValueList(3) = 0 Then TimePercent = 1
    
            DMXData(Channel) = Math.Floor(ValueList(0) + (ValueDistance * TimePercent))
    
        End Function
    
        Private Function CalculateDMXLevel(ByVal Channel As Integer, ByVal CurrentTime As Integer, ByVal ValueList As List(Of Integer))
    
            Dim ValueDistance As Integer = ValueList(1) - ValueList(0)
    
            Dim TimePercent As Single = (CurrentTime - ValueList(2)) / ValueList(3)
    
            If TimePercent > 1 OrElse ValueList(3) = 0 Then TimePercent = 1
    
            DMXData(Channel) = Math.Floor(ValueList(0) + (ValueDistance * TimePercent))
    
        End Function
    
        Private Sub DMXClock_Tick(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
    
            For CounterVarD As Short = 0 To 511
    
                DMXSend(CounterVarD + 1, DMXData(CounterVarD))
    
            Next
    
            Me.BeginInvoke(Sub() DMXListUpdater())
    
        End Sub
    
        Private Sub DMXListUpdater(Optional Channel As Integer = -1)
    
            If Channel > -1 Then
    
                Dim Item As ListViewItem = DMXListView.Items(Channel)
    
                Item.SubItems(1).Text = DMXData(Channel)
    
            Else
    
                For CounterVarD As Short = 0 To 511
    
                    Dim Item As ListViewItem = DMXListView.Items(CounterVarD)
    
                    Item.SubItems(1).Text = DMXData(CounterVarD)
    
                Next
    
            End If
    
        End Sub
    
        Private Sub FinishCue()
    
            DMXClock.Stop()
    
            DMXListUpdater()
    
            Dim LongestChange As Integer
    
            For Each Address In NewValuesList
    
                If Address(2) + Address(3) > LongestChange Then LongestChange = Address(2) + Address(3)
    
            Next
    
            AddressLTimeLabel.Text = LongestChange & "ms"
    
            If SyncTimerList.Count > 0 Then
    
                SynchronousAvgLabel.Text = SyncTimerList.Average & "ms"
    
                SynchronousHLabel.Text = SyncTimerList.Max & "ms"
    
                SynchronousELabel.Text = SyncRunTime & "ms"
    
                SynchronousRLabel.Text = SyncTimerList.Count
    
            End If
    
            If AsyncTimerList.Count > 0 Then
    
                AsynchronousAvgLabel.Text = AsyncTimerList.Average & "ms"
    
                AsynchronousHLabel.Text = AsyncTimerList.Max & "ms"
    
                AsynchronousELabel.Text = AsyncRunTime & "ms"
    
                AsynchronousRLabel.Text = AsyncTimerList.Count
    
            End If
    
            StatusLabel.Text = "Idle"
    
            TestButton.Enabled = True
    
        End Sub
    
    End Class

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width