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 }