gobot.io/x/gobot/v2@v2.1.0/system/system.go (about)

     1  package system
     2  
     3  import (
     4  	"os"
     5  	"syscall"
     6  	"unsafe"
     7  
     8  	"gobot.io/x/gobot/v2"
     9  )
    10  
    11  const systemDebug = false
    12  
    13  // A File represents basic IO interactions with the underlying file system
    14  type File interface {
    15  	Write(b []byte) (n int, err error)
    16  	WriteString(s string) (ret int, err error)
    17  	Sync() (err error)
    18  	Read(b []byte) (n int, err error)
    19  	ReadAt(b []byte, off int64) (n int, err error)
    20  	Seek(offset int64, whence int) (ret int64, err error)
    21  	Fd() uintptr
    22  	Close() error
    23  }
    24  
    25  // filesystem is a unexposed interface to allow the switch between the native file system or a mocked implementation
    26  type filesystem interface {
    27  	openFile(name string, flag int, perm os.FileMode) (file File, err error)
    28  	stat(name string) (os.FileInfo, error)
    29  	find(baseDir string, pattern string) (dirs []string, err error)
    30  	readFile(name string) (content []byte, err error)
    31  }
    32  
    33  // systemCaller represents unexposed Syscall interface to allow the switch between native and mocked implementation
    34  // Prevent unsafe call, since go 1.15, see "Pattern 4" in: https://go101.org/article/unsafe.html
    35  type systemCaller interface {
    36  	syscall(trap uintptr, f File, signal uintptr, payload unsafe.Pointer) (r1, r2 uintptr, err syscall.Errno)
    37  }
    38  
    39  // digitalPinAccesser represents unexposed interface to allow the switch between different implementations and
    40  // a mocked one
    41  type digitalPinAccesser interface {
    42  	isSupported() bool
    43  	createPin(chip string, pin int, o ...func(gobot.DigitalPinOptioner) bool) gobot.DigitalPinner
    44  	setFs(fs filesystem)
    45  }
    46  
    47  // spiAccesser represents unexposed interface to allow the switch between different implementations and a mocked one
    48  type spiAccesser interface {
    49  	isSupported() bool
    50  	createDevice(busNum, chipNum, mode, bits int, maxSpeed int64) (gobot.SpiSystemDevicer, error)
    51  }
    52  
    53  // Accesser provides access to system calls, filesystem, implementation for digital pin and SPI
    54  type Accesser struct {
    55  	sys              systemCaller
    56  	fs               filesystem
    57  	digitalPinAccess digitalPinAccesser
    58  	spiAccess        spiAccesser
    59  }
    60  
    61  // NewAccesser returns a accesser to native system call, native file system and the chosen digital pin access.
    62  // Digital pin accesser can be empty or "sysfs", otherwise it will be automatically chosen.
    63  func NewAccesser(options ...func(Optioner)) *Accesser {
    64  	s := &Accesser{
    65  		sys: &nativeSyscall{},
    66  		fs:  &nativeFilesystem{},
    67  	}
    68  	s.spiAccess = &periphioSpiAccess{fs: s.fs}
    69  	s.digitalPinAccess = &sysfsDigitalPinAccess{fs: s.fs}
    70  	for _, option := range options {
    71  		option(s)
    72  	}
    73  	return s
    74  }
    75  
    76  // UseDigitalPinAccessWithMockFs sets the digital pin handler accesser to the chosen one. Used only for tests.
    77  func (a *Accesser) UseDigitalPinAccessWithMockFs(digitalPinAccess string, files []string) digitalPinAccesser {
    78  	fs := newMockFilesystem(files)
    79  	var dph digitalPinAccesser
    80  	switch digitalPinAccess {
    81  	case "sysfs":
    82  		dph = &sysfsDigitalPinAccess{fs: fs}
    83  	case "cdev":
    84  		dph = &gpiodDigitalPinAccess{fs: fs}
    85  	default:
    86  		dph = &mockDigitalPinAccess{fs: fs}
    87  	}
    88  	a.fs = fs
    89  	a.digitalPinAccess = dph
    90  	return dph
    91  }
    92  
    93  // UseMockSyscall sets the Syscall implementation of the accesser to the mocked one. Used only for tests.
    94  func (a *Accesser) UseMockSyscall() *mockSyscall {
    95  	msc := &mockSyscall{}
    96  	a.sys = msc
    97  	return msc
    98  }
    99  
   100  // UseMockFilesystem sets the filesystem implementation of the accesser to the mocked one. Used only for tests.
   101  func (a *Accesser) UseMockFilesystem(files []string) *MockFilesystem {
   102  	fs := newMockFilesystem(files)
   103  	a.fs = fs
   104  	a.digitalPinAccess.setFs(fs)
   105  	return fs
   106  }
   107  
   108  // UseMockSpi sets the SPI implementation of the accesser to the mocked one. Used only for tests.
   109  func (a *Accesser) UseMockSpi() *MockSpiAccess {
   110  	msc := &MockSpiAccess{}
   111  	a.spiAccess = msc
   112  	return msc
   113  }
   114  
   115  // NewDigitalPin returns a new system digital pin, according to the given pin number.
   116  func (a *Accesser) NewDigitalPin(chip string, pin int,
   117  	o ...func(gobot.DigitalPinOptioner) bool) gobot.DigitalPinner {
   118  	return a.digitalPinAccess.createPin(chip, pin, o...)
   119  }
   120  
   121  // IsSysfsDigitalPinAccess returns whether the used digital pin accesser is a sysfs one.
   122  func (a *Accesser) IsSysfsDigitalPinAccess() bool {
   123  	if _, ok := a.digitalPinAccess.(*sysfsDigitalPinAccess); ok {
   124  		return true
   125  	}
   126  	return false
   127  }
   128  
   129  // NewPWMPin returns a new system PWM pin, according to the given pin number.
   130  func (a *Accesser) NewPWMPin(path string, pin int, polNormIdent string, polInvIdent string) gobot.PWMPinner {
   131  	return newPWMPinSysfs(a.fs, path, pin, polNormIdent, polInvIdent)
   132  }
   133  
   134  // NewSpiDevice returns a new connection to SPI with the given parameters.
   135  func (a *Accesser) NewSpiDevice(busNum, chipNum, mode, bits int, maxSpeed int64) (gobot.SpiSystemDevicer, error) {
   136  	return a.spiAccess.createDevice(busNum, chipNum, mode, bits, maxSpeed)
   137  }
   138  
   139  // OpenFile opens file of given name from native or the mocked file system
   140  func (a *Accesser) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
   141  	return a.fs.openFile(name, flag, perm)
   142  }
   143  
   144  // Stat returns a generic FileInfo, if the file with given name exists. It uses the native or the mocked file system.
   145  func (a *Accesser) Stat(name string) (os.FileInfo, error) {
   146  	return a.fs.stat(name)
   147  }
   148  
   149  // Find finds file from native or the mocked file system
   150  func (a *Accesser) Find(baseDir string, pattern string) ([]string, error) {
   151  	return a.fs.find(baseDir, pattern)
   152  }
   153  
   154  // ReadFile reads the named file and returns the contents. A successful call returns err == nil, not err == EOF.
   155  // Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.
   156  func (a *Accesser) ReadFile(name string) ([]byte, error) {
   157  	return a.fs.readFile(name)
   158  }