Skip to content

Commit

Permalink
feat(tmdl): add multiplexer support
Browse files Browse the repository at this point in the history
  • Loading branch information
ravisuhag committed Feb 12, 2025
1 parent 5198409 commit 0128293
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 0 deletions.
61 changes: 61 additions & 0 deletions pkg/tmdl/mux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package tmdl

import "errors"

// VirtualChannelMultiplexer handles frame scheduling from multiple Virtual Channels.
type VirtualChannelMultiplexer struct {
VChannels map[uint8]*VirtualChannel // Map of VCID to VirtualChannel
Priority map[uint8]int // Scheduling weight per VC
lastUsed uint8 // Tracks last scheduled VC
}

// NewMultiplexer initializes a Virtual Channel multiplexer.
func NewMultiplexer() *VirtualChannelMultiplexer {
return &VirtualChannelMultiplexer{
VChannels: make(map[uint8]*VirtualChannel),
Priority: make(map[uint8]int),
lastUsed: 0,
}
}

// AddVirtualChannel registers a Virtual Channel with a priority weight.
func (mux *VirtualChannelMultiplexer) AddVirtualChannel(vc *VirtualChannel, priority int) {
mux.VChannels[vc.VCID] = vc
mux.Priority[vc.VCID] = priority
}

// GetNextFrame selects the next frame for transmission based on priority.
func (mux *VirtualChannelMultiplexer) GetNextFrame() (*TMTransferFrame, error) {
if len(mux.VChannels) == 0 {
return nil, errors.New("no virtual channels available")
}

// Collect all VCIDs in a slice for round-robin selection
vcids := make([]uint8, 0, len(mux.VChannels))
for vcid := range mux.VChannels {
vcids = append(vcids, vcid)
}

// Find the next eligible VC with available frames
for i := 0; i < len(vcids); i++ {
mux.lastUsed = (mux.lastUsed + 1) % uint8(len(vcids)) // Round-robin selection
vcid := vcids[mux.lastUsed]
vc := mux.VChannels[vcid]

// Check if VC has frames
if vc.HasFrames() {
return vc.GetNextFrame()
}
}
return nil, errors.New("no frames available in any virtual channel")
}

// HasPendingFrames checks if any Virtual Channel has pending frames.
func (mux *VirtualChannelMultiplexer) HasPendingFrames() bool {
for _, vc := range mux.VChannels {
if vc.HasFrames() {
return true
}
}
return false
}
115 changes: 115 additions & 0 deletions pkg/tmdl/mux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package tmdl_test

import (
"github.com/ravisuhag/astro/pkg/tmdl"
"testing"
)

func TestNewMultiplexer(t *testing.T) {
mux := tmdl.NewMultiplexer()

if len(mux.VChannels) != 0 {
t.Errorf("Expected VChannels length 0, got %v", len(mux.VChannels))
}

if len(mux.Priority) != 0 {
t.Errorf("Expected Priority length 0, got %v", len(mux.Priority))
}
}

func TestMultiplexerAddVirtualChannel(t *testing.T) {
mux := tmdl.NewMultiplexer()
vcid := uint8(0x01)
bufferSize := 10
priority := 1

vc := tmdl.NewVirtualChannel(vcid, bufferSize)
mux.AddVirtualChannel(vc, priority)

if len(mux.VChannels) != 1 {
t.Errorf("Expected VChannels length 1, got %v", len(mux.VChannels))
}

if mux.VChannels[vcid] != vc {
t.Errorf("Expected VirtualChannel %v, got %v", vc, mux.VChannels[vcid])
}

if mux.Priority[vcid] != priority {
t.Errorf("Expected Priority %v, got %v", priority, mux.Priority[vcid])
}
}

func TestMultiplexerGetNextFrame(t *testing.T) {
mux := tmdl.NewMultiplexer()
vcid1 := uint8(0x01)
vcid2 := uint8(0x02)
bufferSize := 10
priority := 1

vc1 := tmdl.NewVirtualChannel(vcid1, bufferSize)
vc2 := tmdl.NewVirtualChannel(vcid2, bufferSize)
mux.AddVirtualChannel(vc1, priority)
mux.AddVirtualChannel(vc2, priority)

frame1 := &tmdl.TMTransferFrame{}
frame2 := &tmdl.TMTransferFrame{}
err := vc1.AddFrame(frame1)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
err = vc2.AddFrame(frame2)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

retrievedFrame, err := mux.GetNextFrame()

if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

if retrievedFrame != frame1 && retrievedFrame != frame2 {
t.Errorf("Expected frame1 or frame2, got %v", retrievedFrame)
}

retrievedFrame, err = mux.GetNextFrame()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

if retrievedFrame != frame1 && retrievedFrame != frame2 {
t.Errorf("Expected frame1 or frame2, got %v", retrievedFrame)
}

_, err = mux.GetNextFrame()
if err == nil {
t.Fatalf("Expected error, got nil")
}
}

func TestMultiplexerHasPendingFrames(t *testing.T) {
mux := tmdl.NewMultiplexer()
vcid := uint8(0x01)
bufferSize := 10
priority := 1

vc := tmdl.NewVirtualChannel(vcid, bufferSize)
mux.AddVirtualChannel(vc, priority)

if mux.HasPendingFrames() {
t.Errorf("Expected HasPendingFrames to be false, got true")
}

frame := &tmdl.TMTransferFrame{}
vc.AddFrame(frame)

if !mux.HasPendingFrames() {
t.Errorf("Expected HasPendingFrames to be true, got false")
}

mux.GetNextFrame()

Check failure on line 110 in pkg/tmdl/mux_test.go

View workflow job for this annotation

GitHub Actions / checks

Error return value of `mux.GetNextFrame` is not checked (errcheck)

Check failure on line 110 in pkg/tmdl/mux_test.go

View workflow job for this annotation

GitHub Actions / checks

Error return value of `mux.GetNextFrame` is not checked (errcheck)

if mux.HasPendingFrames() {
t.Errorf("Expected HasPendingFrames to be false, got true")
}
}

0 comments on commit 0128293

Please sign in to comment.