1 /** 2 * Defines channel objects layer. 3 * 4 * A channel is a message delivery mechanism that forwards a message from a 5 * sender to one receiver. 6 */ 7 module dolina.channel; 8 9 import std.exception; 10 import serial.device; 11 12 /** 13 * Defines a basic HostLink channel 14 */ 15 interface IHostLinkChannel { 16 /** 17 * Reads a message from channel 18 * 19 * Returns: message read 20 */ 21 string read(); 22 23 /** 24 * Writes a message on channel 25 * 26 * Params: message = The message being sent on the channel. 27 */ 28 void write(string message); 29 } 30 31 /** 32 * Channel based on serial RS232 communication 33 */ 34 class HostLinkChannel : IHostLinkChannel { 35 private SerialPort serialPort; 36 this(SerialPort serialPort) { 37 enforce(serialPort !is null); 38 this.serialPort = serialPort; 39 } 40 41 string read() { 42 enum START = 0x40; // @ 43 enum END = 0x0D; // CR 44 bool inside; 45 46 ubyte[1] buffer; 47 ubyte[] reply; 48 ubyte b; 49 // dfmt off 50 do { 51 immutable(size_t) length = serialPort.read(buffer); 52 if (length > 0) { 53 b = buffer[0]; 54 if (b == START) { 55 inside = true; 56 } 57 if (inside) { 58 reply ~= b; 59 } 60 } 61 62 } while (b != END); 63 // dfmt on 64 return cast(string)(reply).idup; 65 } 66 67 void write(string message) { 68 serialPort.write(cast(void[])message); 69 } 70 } 71 72 // https://forum.dlang.org/thread/kpvypzrhwbeizzkkamkc@forum.dlang.org 73 //if ( __traits(hasMember, S, "read")) 74 class HLChannel(S = SerialPort) : IHostLinkChannel { 75 static assert(__traits(hasMember, S, "read")); 76 static assert(__traits(hasMember, S, "write")); 77 78 private S serialPort; 79 this(S serialPort) { 80 enforce(serialPort !is null); 81 this.serialPort = serialPort; 82 } 83 84 string read() { 85 enum START = 0x40; // @ 86 enum END = 0x0D; // CR 87 bool inside; 88 89 ubyte[1] buffer; 90 ubyte[] reply; 91 ubyte b; 92 // dfmt off 93 do { 94 immutable(size_t) length = serialPort.read(buffer); 95 if (length > 0) { 96 b = buffer[0]; 97 if (b == START) { 98 inside = true; 99 } 100 if (inside) { 101 reply ~= b; 102 } 103 } 104 } while (b != END); 105 // dfmt on 106 return cast(string)(reply).idup; 107 } 108 109 void write(string message) { 110 serialPort.write(cast(void[])message); 111 } 112 } 113 114 unittest { 115 class SerialMockW { 116 bool writeDone; 117 void write(const(void[]) arr) { 118 writeDone = true; 119 } 120 121 size_t read(void[] arr) { 122 return 3; 123 } 124 } 125 126 auto serial = new SerialMockW(); 127 128 IHostLinkChannel chan = new HLChannel!SerialMockW(serial); 129 assert(!serial.writeDone); 130 chan.write("a"); 131 assert(serial.writeDone); 132 } 133 134 unittest { 135 class SerialMockR { 136 void write(const(void[]) arr) { 137 } 138 139 private ubyte[] buf = [0x39, 0x40, 0x41, 0x0D, 0x43, 0x44]; 140 private size_t ptr; 141 size_t read(void[] arr) { 142 ubyte[] b = cast(ubyte[])arr; 143 b[0] = buf[ptr++]; 144 return 1; 145 } 146 } 147 148 auto serial = new SerialMockR(); 149 150 IHostLinkChannel chan = new HLChannel!SerialMockR(serial); 151 string msg = chan.read(); 152 assert(msg.length == 3); 153 assert(msg == "@A" ~ '\r'); 154 }