-
Jun 5th, 2022, 08:58 AM
#1
Thread Starter
Junior Member
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!
-
Jun 5th, 2022, 10:19 AM
#2
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
-
Jun 6th, 2022, 10:12 AM
#3
Thread Starter
Junior Member
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.
-
Jun 6th, 2022, 10:21 AM
#4
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
-
Jun 6th, 2022, 01:46 PM
#5
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
-
Jun 6th, 2022, 08:58 PM
#6
Thread Starter
Junior Member
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
-
Jun 6th, 2022, 09:10 PM
#7
Thread Starter
Junior Member
Re: Updating 512 Values Over Varrying Times
Originally Posted by dbasnett
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.
-
Jun 6th, 2022, 10:47 PM
#8
Thread Starter
Junior Member
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.
-
Jun 7th, 2022, 10:14 AM
#9
Thread Starter
Junior Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|