1 /// 2 module serialport.config; 3 4 import std.range : split; 5 import std.string : format, toLower; 6 import std.exception : enforce, assertThrown, assertNotThrown; 7 import std.conv : to; 8 9 import serialport.types; 10 import serialport.exception; 11 12 /// 13 struct SPConfig 14 { 15 /// 16 uint baudRate=9600; 17 /// 18 DataBits dataBits=DataBits.data8; 19 /// 20 Parity parity=Parity.none; 21 /// 22 StopBits stopBits=StopBits.one; 23 24 /// 25 bool hardwareDisableFlowControl = true; 26 27 /++ Set parity value 28 Returns: this 29 +/ 30 ref SPConfig set(Parity v) @nogc { parity = v; return this; } 31 32 /++ Set baudrate value 33 Returns: this 34 +/ 35 ref SPConfig set(uint v) @nogc { baudRate = v; return this; } 36 37 /++ Set data bits value 38 Returns: this 39 +/ 40 ref SPConfig set(DataBits v) @nogc { dataBits = v; return this; } 41 42 /++ Set stop bits value 43 Returns: this 44 +/ 45 ref SPConfig set(StopBits v) @nogc { stopBits = v; return this; } 46 47 /++ Use mode string for setting baudrate, data bits, parity and stop bits. 48 49 Format: "B:DPS" 50 where: 51 B is baud rate 52 D is data bits (5, 6, 7, 8) 53 P is parity ('N' or 'n' -- none, 54 'E' or 'e' -- even, 55 'O' or 'o' -- odd) 56 S is stop bits ('1', '1.5', '2') 57 58 You can skip baudrate. 59 60 example mode strings: "9600:8N1" ":8n1" "7o1.5" "2400:6e2" 61 62 Throws: 63 ParseModeException if mode string is badly formatted or using bad values 64 +/ 65 ref SPConfig set(string mode) 66 { 67 alias PME = ParseModeException; 68 69 auto errstr = "error mode '%s'".format(mode); 70 enforce(mode.length >= 3, new PME(errstr ~ ": too short")); 71 72 auto vals = mode.split(modeSplitChar); 73 74 if (vals.length == 0) return this; 75 76 if (vals.length > 2) 77 throw new PME(errstr ~ ": many parts"); 78 79 if (vals.length == 2) 80 { 81 if (vals[0].length) 82 { 83 try baudRate = vals[0].to!uint; 84 catch (Exception e) 85 throw new PME(errstr ~ 86 ": baud rate parse error: " ~ e.msg); 87 } 88 mode = vals[1]; 89 } 90 else mode = vals[0]; 91 92 auto db = cast(int)mode[0] - cast(int)'0'; 93 if (db >= 5 && db <= 8) dataBits = cast(DataBits)db; 94 else throw new PME(errstr ~ ": unsupported data bits '" ~ mode[0] ~ "'"); 95 96 auto p = mode[1..2].toLower; 97 if (p == "n" || p == "o" || p == "e") 98 { 99 parity = ["n": Parity.none, 100 "o": Parity.odd, 101 "e": Parity.even][p]; 102 } 103 else throw new PME(errstr ~ ": unsupported parity '" ~ p ~ "'"); 104 105 auto sb = mode[2..$]; 106 if (sb == "1" || sb == "1.5" || sb == "2") 107 { 108 stopBits = ["1": StopBits.one, 109 "1.5": StopBits.onePointFive, 110 "2": StopBits.two][sb]; 111 } 112 else throw new PME(errstr ~ ": unsupported stop bits '" ~ sb ~ "'"); 113 114 return this; 115 } 116 117 /// 118 unittest 119 { 120 SPConfig c; 121 c.set("2400:7e1.5"); 122 assertNotThrown(c.set(c.mode)); 123 assert(c.baudRate == 2400); 124 assert(c.dataBits == DataBits.data7); 125 assert(c.parity == Parity.even); 126 assert(c.stopBits == StopBits.onePointFive); 127 c.set("8N1"); 128 assertNotThrown(c.set(c.mode)); 129 assert(c.baudRate == 2400); 130 assert(c.dataBits == DataBits.data8); 131 assert(c.parity == Parity.none); 132 assert(c.stopBits == StopBits.one); 133 c.set("320:5o2"); 134 assertNotThrown(c.set(c.mode)); 135 assert(c.baudRate == 320); 136 assert(c.dataBits == DataBits.data5); 137 assert(c.parity == Parity.odd); 138 assert(c.stopBits == StopBits.two); 139 140 alias PME = ParseModeException; 141 assertThrown!PME(c.set("4o2")); 142 assertThrown!PME(c.set("5x2")); 143 assertThrown!PME(c.set("8e3")); 144 assertNotThrown!PME(c.set(":8N1")); 145 assertNotThrown(c.set(c.mode)); 146 } 147 148 /++ Construct config, parse mode to it and return. 149 150 Returns: new config 151 152 See_Also: set(string mode) 153 +/ 154 static SPConfig parse(string mode) 155 { 156 SPConfig ret; 157 ret.set(mode); 158 return ret; 159 } 160 161 /++ Build mode string. 162 163 Can be used for parsing. 164 165 Returns: mode string 166 167 See_Also: parse, set(string mode) 168 +/ 169 string mode() const @property 170 { 171 return "%s:%s%s%s".format( 172 baudRate, 173 dataBits.to!int, 174 [Parity.none: "n", 175 Parity.odd: "o", 176 Parity.even: "e"][parity], 177 [StopBits.one: "1", 178 StopBits.onePointFive: "1.5", 179 StopBits.two: "2" 180 ][stopBits] 181 ); 182 } 183 } 184 185 unittest 186 { 187 SPConfig a, b; 188 a.set("2400:7e2"); 189 b.set(a.mode); 190 assert(a == b); 191 a.set(Parity.none).set(DataBits.data8).set(19200).set(StopBits.one); 192 assert(a.parity == Parity.none); 193 assert(a.dataBits == DataBits.data8); 194 assert(a.baudRate == 19200); 195 assert(a.stopBits == StopBits.one); 196 } 197 198 unittest 199 { 200 SPConfig a; 201 assertThrown!ParseModeException(a.set("2400:7e2:32")); 202 assertThrown!ParseModeException(a.set("24a0:7e2")); 203 }