sim

package
v4.0.0-alpha.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2024 License: MIT Imports: 14 Imported by: 3

Documentation

Overview

Package sim is a package that provides basic functionalities to build discrete event simulator.

Index

Examples

Constants

This section is empty.

Variables

View Source
var HookPosAfterEvent = &HookPos{Name: "AfterEvent"}

HookPosAfterEvent is a hook position that triggers after handling an event.

View Source
var HookPosBeforeEvent = &HookPos{Name: "BeforeEvent"}

HookPosBeforeEvent is a hook position that triggers before handling an event.

View Source
var HookPosBufPop = &HookPos{Name: "Buf Pop"}

HookPosBufPop marks when an element is popped from the buffer.

View Source
var HookPosBufPush = &HookPos{Name: "Buffer Push"}

HookPosBufPush marks when an element is pushed into the buffer.

View Source
var HookPosConnDeliver = &HookPos{Name: "Conn Deliver"}

HookPosConnDeliver marks a connection delivered a message.

View Source
var HookPosConnDoneTrans = &HookPos{Name: "Conn Done Trans"}

HookPosConnDoneTrans marks a connection complete transmitting a message.

View Source
var HookPosConnStartSend = &HookPos{Name: "Conn Start Send"}

HookPosConnStartSend marks a connection accept to send a message.

View Source
var HookPosConnStartTrans = &HookPos{Name: "Conn Start Trans"}

HookPosConnStartTrans marks a connection start to transmit a message.

View Source
var HookPosPortMsgRecvd = &HookPos{Name: "Port Msg Recv"}

HookPosPortMsgRecvd marks when an inbound message arrives at a the given port

View Source
var HookPosPortMsgRetrieve = &HookPos{Name: "Port Msg Retrieve"}

HookPosPortMsgRetrieve marks when an outbound message is sent over a connection

View Source
var HookPosPortMsgSend = &HookPos{Name: "Port Msg Send"}

HookPosPortMsgSend marks when a message is sent out from the port.

Functions

func BuildName

func BuildName(parentName, elementName string) string

BuildName builds a name from a parent name and an element name.

func BuildNameWithIndex

func BuildNameWithIndex(parentName, elementName string, index int) string

BuildNameWithIndex builds a name from a parent name, an element name and an index.

func BuildNameWithMultiDimensionalIndex

func BuildNameWithMultiDimensionalIndex(
	parentName, elementName string,
	index []int,
) string

BuildNameWithMultiDimensionalIndex builds a name from a parent name, an element name and a multi-dimensional index.

func NameMustBeValid

func NameMustBeValid(name string)

NameMustBeValid panics if the name does not follow the naming convention. There are several rules that a name must follow.

  1. It must be organized in a hierarchical structure. For example, a name "A.B.C" is valid, but "A.B.C." is not.
  2. Individual names must not be empty. For example, "A..B" is not valid.
  3. Individual names must be named as capitalized CamelCase style. For example, "A.b" is not valid.
  4. Elements in a series must be named using square-bracket notation.

func NewSampleMsg

func NewSampleMsg() *sampleMsg

func UseParallelIDGenerator

func UseParallelIDGenerator()

UseParallelIDGenerator configurs the ID generator to generate ID in parallel. The IDs generated will not be deterministic anymore.

func UseSequentialIDGenerator

func UseSequentialIDGenerator()

UseSequentialIDGenerator configures the ID generator to generate IDs in sequential.

Types

type Buffer

type Buffer interface {
	Named
	Hookable

	CanPush() bool
	Push(e interface{})
	Pop() interface{}
	Peek() interface{}
	Capacity() int
	Size() int

	// Remove all elements in the buffer
	Clear()
}

A Buffer is a fifo queue for anything

func NewBuffer

func NewBuffer(name string, capacity int) Buffer

NewBuffer creates a default buffer object.

type Component

type Component interface {
	Named
	Handler
	Hookable
	PortOwner

	NotifyRecv(port Port)
	NotifyPortFree(port Port)
}

A Component is a element that is being simulated in Akita.

type ComponentBase

type ComponentBase struct {
	sync.Mutex
	HookableBase
	*PortOwnerBase
	// contains filtered or unexported fields
}

ComponentBase provides some functions that other component can use.

func NewComponentBase

func NewComponentBase(name string) *ComponentBase

NewComponentBase creates a new ComponentBase

func (*ComponentBase) Name

func (c *ComponentBase) Name() string

Name returns the name of the BasicComponent

type Connection

type Connection interface {
	Named
	Hookable

	PlugIn(port Port)
	Unplug(port Port)
	NotifyAvailable(port Port)
	NotifySend()
}

A Connection is responsible for delivering messages to its destination.

type Domain

type Domain struct {
	*PortOwnerBase
	// contains filtered or unexported fields
}

Domain is a group of components that are closely connected.

func NewDomain

func NewDomain(name string) *Domain

NewDomain creates a new Domain

func (Domain) Name

func (d Domain) Name() string

Name returns the name of the domain.

type Engine

type Engine interface {
	Hookable
	EventScheduler

	// Run will process all the events until the simulation finishes
	Run() error

	// Pause will pause the simulation until continue is called.
	Pause()

	// Continue will continue the paused simulation
	Continue()
}

An Engine is a unit that keeps the discrete event simulation run.

type Event

type Event interface {
	// Return the time that the event should happen
	Time() VTimeInSec

	// Returns the handler that can should handle the event
	Handler() Handler

	// IsSecondary tells if the event is a secondary event. Secondary event are
	// handled after all same-time primary events are handled.
	IsSecondary() bool
}

An Event is something going to happen in the future.

Example
package main

import (
	"fmt"
	"math/rand"

	"github.com/sarchlab/akita/v4/sim"
)

type SplitEvent struct {
	time    sim.VTimeInSec
	handler sim.Handler
}

func (e SplitEvent) Time() sim.VTimeInSec {
	return e.time
}
func (e SplitEvent) Handler() sim.Handler {
	return e.handler
}
func (e SplitEvent) IsSecondary() bool {
	return false
}

type SplitHandler struct {
	total  int
	engine sim.Engine
}

func (h *SplitHandler) Handle(evt sim.Event) error {
	h.total++
	now := evt.Time()
	nextTime := now + sim.VTimeInSec(rand.Float64()*2+0.5)

	if nextTime < 10.0 {
		nextEvt := SplitEvent{
			time:    nextTime,
			handler: h,
		}
		h.engine.Schedule(nextEvt)
	}

	nextTime = now + sim.VTimeInSec(rand.Float64()*2+0.5)
	if nextTime < 10.0 {
		nextEvt := SplitEvent{
			time:    nextTime,
			handler: h,
		}
		h.engine.Schedule(nextEvt)
	}

	return nil
}

func main() {
	rand.Seed(1)

	engine := sim.NewSerialEngine()

	splitHandler := SplitHandler{
		total:  0,
		engine: engine,
	}
	engine.Schedule(SplitEvent{
		time:    0,
		handler: &splitHandler,
	})
	engine.Run()
	fmt.Printf("Total number at time 10: %d\n", splitHandler.total)
}
Output:

Total number at time 10: 185

type EventBase

type EventBase struct {
	ID string
	// contains filtered or unexported fields
}

EventBase provides the basic fields and getters for other events

func NewEventBase

func NewEventBase(t VTimeInSec, handler Handler) *EventBase

NewEventBase creates a new EventBase

func (EventBase) Handler

func (e EventBase) Handler() Handler

Handler returns the handler to handle the event.

func (EventBase) IsSecondary

func (e EventBase) IsSecondary() bool

IsSecondary returns true if the event is a secondary event.

func (EventBase) Time

func (e EventBase) Time() VTimeInSec

Time return the time that the event is going to happen

type EventLogger

type EventLogger struct {
	LogHookBase
}

EventLogger is an hook that prints the event information

func NewEventLogger

func NewEventLogger(logger *log.Logger) *EventLogger

NewEventLogger returns a new LogEventHook which will write in to the logger

func (*EventLogger) Func

func (h *EventLogger) Func(ctx HookCtx)

Func writes the event information into the logger

type EventQueue

type EventQueue interface {
	Push(evt Event)
	Pop() Event
	Len() int
	Peek() Event
}

EventQueue are a queue of event ordered by the time of events

type EventQueueImpl

type EventQueueImpl struct {
	sync.Mutex
	// contains filtered or unexported fields
}

EventQueueImpl provides a thread safe event queue

func NewEventQueue

func NewEventQueue() *EventQueueImpl

NewEventQueue creates and returns a newly created EventQueue

func (*EventQueueImpl) Len

func (q *EventQueueImpl) Len() int

Len returns the number of event in the queue

func (*EventQueueImpl) Peek

func (q *EventQueueImpl) Peek() Event

Peek returns the event in front of the queue without removing it from the queue

func (*EventQueueImpl) Pop

func (q *EventQueueImpl) Pop() Event

Pop returns the next earliest event

func (*EventQueueImpl) Push

func (q *EventQueueImpl) Push(evt Event)

Push adds an event to the event queue

type EventScheduler

type EventScheduler interface {
	TimeTeller

	Schedule(e Event)
}

EventScheduler can be used to schedule future events.

type Freq

type Freq float64

Freq defines the type of frequency

const (
	Hz  Freq = 1
	KHz Freq = 1e3
	MHz Freq = 1e6
	GHz Freq = 1e9
)

Defines the unit of frequency

func (Freq) Cycle

func (f Freq) Cycle(time VTimeInSec) uint64

Cycle converts a time to the number of cycles passed since time 0.

func (Freq) HalfTick

func (f Freq) HalfTick(t VTimeInSec) VTimeInSec

HalfTick returns the time in middle of two ticks

           Input
           (          ]
|----------|----------|----------|----->
                           |
                           Output

func (Freq) NCyclesLater

func (f Freq) NCyclesLater(n int, now VTimeInSec) VTimeInSec

NCyclesLater returns the time after N cycles

This function will always return a time of an integer number of cycles

func (Freq) NextTick

func (f Freq) NextTick(now VTimeInSec) VTimeInSec

NextTick returns the next tick time.

           Input
           [          )
|----------|----------|----------|----->
                      |
                      Output

func (Freq) NoEarlierThan

func (f Freq) NoEarlierThan(t VTimeInSec) VTimeInSec

NoEarlierThan returns the tick time that is at or right after the given time

func (Freq) Period

func (f Freq) Period() VTimeInSec

Period returns the time between two consecutive ticks

func (Freq) ThisTick

func (f Freq) ThisTick(now VTimeInSec) VTimeInSec

ThisTick returns the current tick time

           Input
           (          ]
|----------|----------|----------|----->
                      |
                      Output

type GeneralRsp

type GeneralRsp struct {
	MsgMeta

	OriginalReq Msg
}

GeneralRsp is a general response message that is used to indicate the completion of a request.

func (*GeneralRsp) Clone

func (r *GeneralRsp) Clone() Msg

Clone returns cloned GeneralRsp with different ID

func (*GeneralRsp) GetRspTo

func (r *GeneralRsp) GetRspTo() string

GetRspTo returns the ID of the original request.

func (*GeneralRsp) Meta

func (r *GeneralRsp) Meta() *MsgMeta

Meta returns the meta data of the message.

type GeneralRspBuilder

type GeneralRspBuilder struct {
	Src, Dst     RemotePort
	TrafficClass int
	TrafficBytes int
	OriginalReq  Msg
}

GeneralRspBuilder can build general response messages.

func (GeneralRspBuilder) Build

func (c GeneralRspBuilder) Build() *GeneralRsp

Build creates a new general response message.

func (GeneralRspBuilder) WithDst

WithDst sets the destination of the general response message.

func (GeneralRspBuilder) WithOriginalReq

func (c GeneralRspBuilder) WithOriginalReq(originalReq Msg) GeneralRspBuilder

WithOriginalReq sets the original request of the general response message.

func (GeneralRspBuilder) WithSrc

WithSrc sets the source of the general response message.

func (GeneralRspBuilder) WithTrafficBytes

func (c GeneralRspBuilder) WithTrafficBytes(
	trafficBytes int,
) GeneralRspBuilder

WithTrafficBytes sets the traffic bytes of the general response message.

func (GeneralRspBuilder) WithTrafficClass

func (c GeneralRspBuilder) WithTrafficClass(
	trafficClass int,
) GeneralRspBuilder

WithTrafficClass sets the traffic class of the general response message.

type Handler

type Handler interface {
	Handle(e Event) error
}

A Handler defines a domain for the events.

One event is always constraint to one Handler, which means the event can only be scheduled by one handler and can only directly modify that handler.

type Hook

type Hook interface {
	// Func determines what to do if hook is invoked.
	Func(ctx HookCtx)
}

Hook is a short piece of program that can be invoked by a hookable object.

type HookCtx

type HookCtx struct {
	Domain Hookable
	Pos    *HookPos
	Item   interface{}
	Detail interface{}
}

HookCtx is the context that holds all the information about the site that a hook is triggered.

type HookPos

type HookPos struct {
	Name string
}

HookPos defines the enum of possible hooking positions.

type Hookable

type Hookable interface {
	// AcceptHook registers a hook.
	AcceptHook(hook Hook)

	// NumHooks returns the number of hooks registered.
	NumHooks() int

	// Hooks returns all the hooks registered.
	Hooks() []Hook
}

Hookable defines an object that accept Hooks.

type HookableBase

type HookableBase struct {
	// contains filtered or unexported fields
}

A HookableBase provides some utility function for other type that implement the Hookable interface.

func NewHookableBase

func NewHookableBase() *HookableBase

NewHookableBase creates a HookableBase object.

func (*HookableBase) AcceptHook

func (h *HookableBase) AcceptHook(hook Hook)

AcceptHook register a hook.

func (*HookableBase) Hooks

func (h *HookableBase) Hooks() []Hook

Hooks returns all the hooks registered.

func (*HookableBase) InvokeHook

func (h *HookableBase) InvokeHook(ctx HookCtx)

InvokeHook triggers the register Hooks.

func (*HookableBase) NumHooks

func (h *HookableBase) NumHooks() int

NumHooks returns the number of hooks registered.

type IDGenerator

type IDGenerator interface {
	// Generate an ID
	Generate() string
}

IDGenerator can generate IDs

func GetIDGenerator

func GetIDGenerator() IDGenerator

GetIDGenerator returns the ID generator used in the current simulation

type InsertionQueue

type InsertionQueue struct {
	// contains filtered or unexported fields
}

InsertionQueue is a queue that is based on insertion sort

func NewInsertionQueue

func NewInsertionQueue() *InsertionQueue

NewInsertionQueue returns a new InsertionQueue

func (*InsertionQueue) Len

func (q *InsertionQueue) Len() int

Len return the number of events in the queue

func (*InsertionQueue) Peek

func (q *InsertionQueue) Peek() Event

Peek returns the event at the front of the queue without removing it from the queue.

func (*InsertionQueue) Pop

func (q *InsertionQueue) Pop() Event

Pop returns the event with the smallest time, and removes it from the queue

func (*InsertionQueue) Push

func (q *InsertionQueue) Push(evt Event)

Push add an event to the event queue

type LogHook

type LogHook interface {
	Hook
}

A LogHook is a hook that is resonsible for recording information from the simulation

type LogHookBase

type LogHookBase struct {
	*log.Logger
}

LogHookBase proovides the common logic for all LogHooks

type Middleware

type Middleware interface {
	// Tick processes a tick event. It returns true if progress is made.
	Tick() bool
}

Middleware defines the actions of a component.

type MiddlewareHolder

type MiddlewareHolder struct {
	// contains filtered or unexported fields
}

MiddlewareHolder can maintain a list of middleware.

func (*MiddlewareHolder) AddMiddleware

func (holder *MiddlewareHolder) AddMiddleware(middleware Middleware)

AddMiddleware adds a middleware to the holder.

func (*MiddlewareHolder) Middlewares

func (holder *MiddlewareHolder) Middlewares() []Middleware

Middlewares returns the list of middleware.

func (*MiddlewareHolder) Tick

func (holder *MiddlewareHolder) Tick() bool

Tick processes a tick event. It returns true if progress is made.

type Msg

type Msg interface {
	Meta() *MsgMeta
	Clone() Msg
}

A Msg is a piece of information that is transferred between components.

type MsgMeta

type MsgMeta struct {
	ID           string
	Src, Dst     RemotePort
	TrafficClass int
	TrafficBytes int
}

MsgMeta contains the meta data that is attached to every message.

type Name

type Name struct {
	Tokens []NameToken
}

A Name is a hierarchical name that includes a series of tokens separated by dots.

func ParseName

func ParseName(sname string) Name

ParseName parses a name string and returns a Name object.

type NameToken

type NameToken struct {
	ElemName string
	Index    []int
}

NameToken is a token of a name.

type Named

type Named interface {
	Name() string
}

A Named object is an object that has a name.

type ParallelEngine

type ParallelEngine struct {
	HookableBase
	// contains filtered or unexported fields
}

A ParallelEngine is an event engine that is capable for scheduling event in a parallel fashion

func NewParallelEngine

func NewParallelEngine() *ParallelEngine

NewParallelEngine creates a ParallelEngine

func (*ParallelEngine) Continue

func (e *ParallelEngine) Continue()

Continue allows the engine to continue to make progress.

func (*ParallelEngine) CurrentTime

func (e *ParallelEngine) CurrentTime() VTimeInSec

CurrentTime returns the current time at which the engine is at. Specifically, the run time of the current event.

func (*ParallelEngine) Pause

func (e *ParallelEngine) Pause()

Pause will prevent the engine to move forward. For events that is scheduled at the same time, they may still be triggered.

func (*ParallelEngine) Run

func (e *ParallelEngine) Run() error

Run processes all the events scheduled in the SerialEngine

func (*ParallelEngine) Schedule

func (e *ParallelEngine) Schedule(evt Event)

Schedule register an event to be happen in the future

type Port

type Port interface {
	Named
	Hookable

	AsRemote() RemotePort

	SetConnection(conn Connection)
	Component() Component

	// For connection
	Deliver(msg Msg) *SendError
	NotifyAvailable()
	RetrieveOutgoing() Msg
	PeekOutgoing() Msg

	// For component
	CanSend() bool
	Send(msg Msg) *SendError
	RetrieveIncoming() Msg
	PeekIncoming() Msg
}

A Port is owned by a component and is used to plugin connections

func NewPort

func NewPort(
	comp Component,
	incomingBufCap, outgoingBufCap int,
	name string,
) Port

NewPort creates a new port with default behavior.

type PortMsgLogger

type PortMsgLogger struct {
	LogHookBase
	TimeTeller
}

PortMsgLogger is a hook for logging messages as they go across a Port

func NewPortMsgLogger

func NewPortMsgLogger(
	logger *log.Logger,
	timeTeller TimeTeller,
) *PortMsgLogger

NewPortMsgLogger returns a new PortMsgLogger which will write into the logger

func (*PortMsgLogger) Func

func (h *PortMsgLogger) Func(ctx HookCtx)

Func writes the message information into the logger

type PortOwner

type PortOwner interface {
	AddPort(name string, port Port)
	GetPortByName(name string) Port
	Ports() []Port
}

A PortOwner is an element that can communicate with others through ports.

type PortOwnerBase

type PortOwnerBase struct {
	// contains filtered or unexported fields
}

PortOwnerBase provides an implementation of the PortOwner interface.

func NewPortOwnerBase

func NewPortOwnerBase() *PortOwnerBase

NewPortOwnerBase creates a new PortOwnerBase

func (*PortOwnerBase) AddPort

func (po *PortOwnerBase) AddPort(name string, port Port)

AddPort adds a new port with a given name.

func (PortOwnerBase) GetPortByName

func (po PortOwnerBase) GetPortByName(name string) Port

GetPortByName returns the port according to the name of the port. This function panics when the given name is not found.

func (PortOwnerBase) Ports

func (po PortOwnerBase) Ports() []Port

Ports returns a slices of all the ports owned by the PortOwner.

type RemotePort

type RemotePort string

A RemotePort is a string that refers to another port.

type Request

type Request interface {
	Msg
	GenerateRsp() Rsp
}

type Rsp

type Rsp interface {
	Msg
	GetRspTo() string
}

Rsp is a special message that is used to indicate the completion of a request.

type SendError

type SendError struct{}

SendError marks a failure send or receive

func NewSendError

func NewSendError() *SendError

NewSendError creates a SendError

type SerialEngine

type SerialEngine struct {
	HookableBase
	// contains filtered or unexported fields
}

A SerialEngine is an Engine that always run events one after another.

func NewSerialEngine

func NewSerialEngine() *SerialEngine

NewSerialEngine creates a SerialEngine

func (*SerialEngine) Continue

func (e *SerialEngine) Continue()

Continue allows the SerialEngine to trigger more events.

func (*SerialEngine) CurrentTime

func (e *SerialEngine) CurrentTime() VTimeInSec

CurrentTime returns the current time at which the engine is at. Specifically, the run time of the current event.

func (*SerialEngine) Pause

func (e *SerialEngine) Pause()

Pause prevents the SerialEngine to trigger more events.

func (*SerialEngine) Run

func (e *SerialEngine) Run() error

Run processes all the events scheduled in the SerialEngine

func (*SerialEngine) Schedule

func (e *SerialEngine) Schedule(evt Event)

Schedule register an event to be happen in the future

type Simulation

type Simulation struct {
	// contains filtered or unexported fields
}

A Simulation provides the service requires to define a simulation.

func NewSimulation

func NewSimulation() *Simulation

NewSimulation creates a new simulation.

func (*Simulation) GetComponentByName

func (s *Simulation) GetComponentByName(name string) Component

GetComponentByName returns the component with the given name.

func (*Simulation) GetEngine

func (s *Simulation) GetEngine() Engine

GetEngine returns the engine used in the simulation.

func (*Simulation) GetPortByName

func (s *Simulation) GetPortByName(name string) Port

GetPortByName returns the port with the given name.

func (*Simulation) RegisterComponent

func (s *Simulation) RegisterComponent(c Component)

RegisterComponent registers a component with the simulation.

func (*Simulation) RegisterEngine

func (s *Simulation) RegisterEngine(e Engine)

RegisterEngine registers the engine used in the simulation.

type TickEvent

type TickEvent struct {
	EventBase
}

TickEvent is a generic event that almost all the component can use to update their status.

func MakeTickEvent

func MakeTickEvent(handler Handler, time VTimeInSec) TickEvent

MakeTickEvent creates a new TickEvent

type TickScheduler

type TickScheduler struct {
	Freq   Freq
	Engine Engine
	// contains filtered or unexported fields
}

TickScheduler can help schedule tick events.

func NewSecondaryTickScheduler

func NewSecondaryTickScheduler(
	handler Handler,
	engine Engine,
	freq Freq,
) *TickScheduler

NewSecondaryTickScheduler creates a scheduler that always schedule secondary tick events.

func NewTickScheduler

func NewTickScheduler(
	handler Handler,
	engine Engine,
	freq Freq,
) *TickScheduler

NewTickScheduler creates a scheduler for tick events.

func (*TickScheduler) CurrentTime

func (t *TickScheduler) CurrentTime() VTimeInSec

func (*TickScheduler) TickLater

func (t *TickScheduler) TickLater()

TickLater will schedule a tick event at the cycle after the now time.

func (*TickScheduler) TickNow

func (t *TickScheduler) TickNow()

TickNow schedule a Tick event at the current time.

type Ticker

type Ticker interface {
	Tick() bool
}

A Ticker is an object that updates states with ticks.

type TickingComponent

type TickingComponent struct {
	*ComponentBase
	*TickScheduler
	// contains filtered or unexported fields
}

TickingComponent is a type of component that update states from cycle to cycle. A programmer would only need to program a tick function for a ticking component.

func NewSecondaryTickingComponent

func NewSecondaryTickingComponent(
	name string,
	engine Engine,
	freq Freq,
	ticker Ticker,
) *TickingComponent

NewSecondaryTickingComponent creates a new ticking component

func NewTickingComponent

func NewTickingComponent(
	name string,
	engine Engine,
	freq Freq,
	ticker Ticker,
) *TickingComponent

NewTickingComponent creates a new ticking component

func (*TickingComponent) Handle

func (c *TickingComponent) Handle(e Event) error

Handle triggers the tick function of the TickingComponent

func (*TickingComponent) NotifyPortFree

func (c *TickingComponent) NotifyPortFree(
	_ Port,
)

NotifyPortFree triggers the TickingComponent to start ticking again.

func (*TickingComponent) NotifyRecv

func (c *TickingComponent) NotifyRecv(
	_ Port,
)

NotifyRecv triggers the TickingComponent to start ticking again.

type TimeTeller

type TimeTeller interface {
	CurrentTime() VTimeInSec
}

TimeTeller can be used to get the current time.

type VTimeInSec

type VTimeInSec float64

VTimeInSec defines the time in the simulated space in the unit of second

Directories

Path Synopsis
Package directconnection provides directconnection
Package directconnection provides directconnection
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL