1 /// 2 module serialport.fiberready; 3 4 import serialport.base; 5 6 import std.traits : isSomeFunction, 7 FunctionAttribute, 8 functionAttributes; 9 10 /++ Serial Port Fiber Ready 11 +/ 12 class SerialPortFR : SerialPort 13 { 14 protected: 15 16 /++ Preform pause 17 18 If sleepFunc isn't null call it. Else use `Thread.sleep` or 19 `Fiber.yield` if code executes in fiber. 20 21 Params: 22 dt = sleep time 23 +/ 24 void sleep(Duration dt) @nogc 25 { 26 if (_sleepFunc !is null) _sleepFunc(dt); 27 else 28 { 29 import core.thread : Fiber, Thread; 30 if (Fiber.getThis is null) Thread.sleep(dt); 31 else 32 { 33 const tm = StopWatch(AutoStart.yes); 34 do Fiber.yield(); while (tm.peek < dt); 35 } 36 } 37 } 38 39 /++ Calc pause for sleep in read and write loops 40 +/ 41 Duration ioPause() @nogc 42 { 43 auto cfg = config; 44 auto cnt = 1 + // start bit 45 cast(int)cfg.dataBits + 46 (cfg.parity == Parity.none ? 0 : 1) + 47 (cfg.stopBits == StopBits.one ? 1 : 48 cfg.stopBits == StopBits.onePointFive ? 1.5 : 2) + 49 1.5 // reserve 50 ; 51 return (cast(ulong)(cnt / cfg.baudRate * 1e6) + 100/+reserve+/).usecs; 52 } 53 54 void delegate(Duration) @nogc _sleepFunc; 55 56 public: 57 /// assume @nogc 58 deprecated 59 alias SleepFunc = void delegate(Duration); 60 61 /// 62 alias SleepFuncNoGC = void delegate(Duration) @nogc; 63 64 /// extended delegate for perform sleep 65 deprecated("sleep function must be @nogc") 66 void sleepFunc(SleepFunc dlg) @property 67 { _sleepFunc = cast(void delegate(Duration) @nogc)dlg; } 68 69 /// 70 deprecated("sleep function must be @nogc") 71 void sleepFunc(void function(Duration) fnc) @property 72 { _sleepFunc = (d){ (cast(void function(Duration) @nogc)fnc)(d); }; } 73 74 /// extended delegate for perform sleep 75 void sleepFunc(void delegate(Duration) @nogc dlg) @property 76 { _sleepFunc = dlg; } 77 78 /// ditto 79 void sleepFunc(void function(Duration) @nogc fnc) @property 80 { _sleepFunc = (d){ fnc(d); }; } 81 82 /// ditto 83 SleepFuncNoGC sleepFunc() @property { return _sleepFunc; } 84 85 /++ Construct SerialPortFR 86 87 See_Also: SerialPort.this 88 +/ 89 deprecated("sleep function must be @nogc") 90 this(F=SleepFunc)(string exmode, F sf) 91 if (isSomeFunction!F && !(functionAttributes!F & FunctionAttribute.nogc)) 92 { sleepFunc = sf; super(exmode); } 93 94 /// ditto 95 deprecated("sleep function must be @nogc") 96 this(F=SleepFunc)(string port, string mode, F sf) 97 if (isSomeFunction!F && !(functionAttributes!F & FunctionAttribute.nogc)) 98 { sleepFunc = sf; super(port, mode); } 99 100 /// ditto 101 deprecated("sleep function must be @nogc") 102 this(F=SleepFunc)(string port, uint baudRate, F sf) 103 if (isSomeFunction!F && !(functionAttributes!F & FunctionAttribute.nogc)) 104 { sleepFunc = sf; super(port, baudRate); } 105 106 /// ditto 107 deprecated("sleep function must be @nogc") 108 this(F=SleepFunc)(string port, uint baudRate, string mode, F sf) 109 if (isSomeFunction!F && !(functionAttributes!F & FunctionAttribute.nogc)) 110 { sleepFunc = sf; super(port, baudRate, mode); } 111 112 /// ditto 113 deprecated("sleep function must be @nogc") 114 this(F=SleepFunc)(string port, Config conf, F sf) 115 if (isSomeFunction!F && !(functionAttributes!F & FunctionAttribute.nogc)) 116 { sleepFunc = sf; super(port, conf); } 117 118 /// ditto 119 this(F=SleepFuncNoGC)(string exmode, F sf=null) 120 if (isSomeFunction!F) 121 { sleepFunc = sf; super(exmode); } 122 123 /// ditto 124 this(F=SleepFuncNoGC)(string port, string mode, F sf=null) 125 if (isSomeFunction!F && (functionAttributes!F & FunctionAttribute.nogc)) 126 { sleepFunc = sf; super(port, mode); } 127 128 /// ditto 129 this(F=SleepFuncNoGC)(string port, uint baudRate, F sf=null) 130 if (isSomeFunction!F && (functionAttributes!F & FunctionAttribute.nogc)) 131 { sleepFunc = sf; super(port, baudRate); } 132 133 /// ditto 134 this(F=SleepFuncNoGC)(string port, uint baudRate, string mode, F sf=null) 135 if (isSomeFunction!F && (functionAttributes!F & FunctionAttribute.nogc)) 136 { sleepFunc = sf; super(port, baudRate, mode); } 137 138 /// ditto 139 this(F=SleepFuncNoGC)(string port, Config conf, F sf=null) 140 if (isSomeFunction!F && (functionAttributes!F & FunctionAttribute.nogc)) 141 { sleepFunc = sf; super(port, conf); } 142 143 override void[] read(void[] buf, CanRead cr=CanRead.allOrNothing) 144 { 145 if (closed) throwPortClosedException(port); 146 147 size_t res; 148 const timeout = buf.length * readTimeoutMult + readTimeout; 149 const pause = ioPause(); 150 const sw = StopWatch(AutoStart.yes); 151 while (sw.peek < timeout) 152 { 153 res += m_read(buf[res..$]).length; 154 if (res == buf.length) return buf[]; 155 this.sleep(pause); 156 } 157 158 checkAbility(cr, res, buf.length); 159 160 return buf[0..res]; 161 } 162 163 override void write(const(void[]) arr) 164 { 165 if (closed) throwPortClosedException(port); 166 167 size_t written; 168 const timeout = arr.length * writeTimeoutMult + writeTimeout; 169 const pause = ioPause(); 170 const sw = StopWatch(AutoStart.yes); 171 while (sw.peek < timeout) 172 { 173 written += m_write(arr[written..$]); 174 if (written == arr.length) return; 175 this.sleep(pause); 176 } 177 178 throwTimeoutException(port, "write timeout"); 179 } 180 181 /++ Read data while available by parts, sleep between checks. 182 183 Sleep time calculates from baud rate and count of bits in one byte. 184 185 ------- 186 ---|-----|-----|------------|-----|------------> t 187 call | | | | 188 readAll | | | | 189 | | | | | 190 | |<---------data receive---------->| 191 | |=== ===== ======| | |== =| 192 | | | | | | | 193 |<-timeout->| | | | | 194 | |<-1->| |<2>| |<-3->| 195 | | | | 196 | |<---readedData--->| | 197 | return 198 |<------readAll work time----->| 199 200 (1) if readedData.length > 0 then continue reading 201 else throw TimeoutException 202 (2) silent time, if silent < frameGap then continue reading 203 (3) else if silent > frameGap then stop reading 204 and return readedData 205 ------- 206 207 Params: 208 buf = buffer for reading 209 startTimeout = timeout for first byte recive 210 frameGap = detect new data frame by silence period 211 212 Returns: slice of buf with readed data 213 214 Throws: 215 PortClosedException 216 ReadException 217 TimeoutException 218 219 See_Also: SerialPort.read 220 +/ 221 void[] readContinues(void[] buf, Duration startTimeout=1.seconds, 222 Duration frameGap=50.msecs) 223 { 224 if (closed) throwPortClosedException(port); 225 226 ptrdiff_t readed; 227 228 auto pause = ioPause(); 229 230 StopWatch silence, full; 231 232 full.start(); 233 while (true) 234 { 235 const res = m_read(buf[readed..$]).length; 236 237 readed += res; 238 239 // buffer filled 240 if (readed == buf.length) return buf[]; 241 242 if (res == 0) 243 { 244 if (readed > 0 && silence.peek > frameGap) 245 return buf[0..readed]; 246 247 if (!silence.running) silence.start(); 248 } 249 else 250 { 251 silence.stop(); 252 silence.reset(); 253 } 254 255 if (readed == 0 && full.peek > startTimeout) 256 throwTimeoutException(port, "read timeout"); 257 258 this.sleep(pause); 259 } 260 } 261 }