1 module modbus.protocol; 2 3 import std.exception : enforce; 4 import std.string; 5 import std.bitmanip; 6 import std.traits : isArray, Unqual, isNumeric; 7 import std.range : ElementType; 8 import std.experimental.logger; 9 10 public import modbus.exception; 11 12 import modbus.func; 13 14 class Modbus 15 { 16 protected: 17 Backend be; 18 19 public: 20 21 static interface Backend 22 { 23 void start(ubyte dev, ubyte func); 24 25 void append(byte); 26 void append(ubyte); 27 void append(short); 28 void append(ushort); 29 void append(int); 30 void append(uint); 31 void append(long); 32 void append(ulong); 33 void append(float); 34 void append(double); 35 void append(const(void)[]); 36 37 bool messageComplite() const @property; 38 const(void)[] tempBuffer() const @property; 39 40 void send(); 41 42 static struct Response 43 { 44 ubyte dev, fnc; 45 const(void)[] data; 46 } 47 48 Response read(size_t expectedBytes); 49 } 50 51 invariant 52 { 53 if (be !is null) 54 assert(be.messageComplite); 55 } 56 57 this(Backend be) { this.be = enforce(be, "backend is null"); } 58 59 bool needCheckCRC = true; 60 61 void write(Args...)(ubyte dev, ubyte func, Args args) 62 { 63 be.start(dev, func); 64 65 void _append(T)(T val) 66 { 67 static if (isArray!T) 68 { 69 static if (is(Unqual!(ElementType!T) == void)) 70 be.append(val); 71 else foreach (e; val) _append(e); 72 } 73 else 74 { 75 static if (is(T == struct)) 76 foreach (name; __traits(allMembers, T)) 77 _append(__traits(getMember, val, name)); 78 else static if (isNumeric!T) be.append(val); 79 else static assert(0, "unsupported type " ~ T.stringof); 80 } 81 } 82 83 foreach (arg; args) _append(arg); 84 85 be.send(); 86 } 87 88 // result in big endian 89 const(void)[] read(size_t bytes, ubyte dev, ubyte fnc) 90 { 91 auto res = be.read(bytes); 92 93 if (res.dev != dev) 94 .warningf("receive from unexpected device %d (expect %d)", 95 res.dev, dev); 96 97 enforce(res.fnc == fnc, 98 new FunctionErrorException(dev, fnc, res.fnc, (cast(ubyte[])res.data)[0])); 99 100 enforce(res.data.length == bytes, 101 new ReadDataLengthException(dev, fnc, bytes, res.data.length)); 102 103 return res.data; 104 } 105 106 const(BitArray) readCoils(ubyte dev, ushort start, ushort cnt) 107 { 108 enforce(cnt <= 2000, "very big count"); 109 this.write(dev, 1, start, cnt); 110 111 return const(BitArray)(cast(void[])this.read(1+(cnt+7)/8, dev, 1)[1..$], cnt); 112 } 113 114 const(BitArray) readDiscreteInputs(ubyte dev, ushort start, ushort cnt) 115 { 116 enforce(cnt <= 2000, "very big count"); 117 this.write(dev, 2, start, cnt); 118 return const(BitArray)(cast(void[])this.read(1+(cnt+7)/8, dev, 2)[1..$], cnt); 119 } 120 121 ushort[] readHoldingRegisters(ubyte dev, ushort start, ushort cnt) 122 { 123 enforce(cnt <= 125, "very big count"); 124 this.write(dev, 3, start, cnt); 125 auto res = this.read(1+cnt*2, dev, 3); 126 return bigEndianToNativeArr(cast(ushort[])res[1..$]); 127 } 128 129 ushort[] readInputRegisters(ubyte dev, ushort start, ushort cnt) 130 { 131 enforce(cnt <= 125, "very big count"); 132 this.write(dev, 4, start, cnt); 133 auto res = this.read(1+cnt*2, dev, 4); 134 return bigEndianToNativeArr(cast(ushort[])res[1..$]); 135 } 136 137 void writeSingleCoil(ubyte dev, ushort addr, bool val) 138 { 139 this.write(dev, 5, addr, cast(ushort)(val ? 0xff00: 0x0000)); 140 this.read(4, dev, 5); 141 } 142 143 void writeSingleRegister(ubyte dev, ushort addr, ushort value) 144 { 145 this.write(dev, 6, addr, value); 146 this.read(4, dev, 6); 147 } 148 149 void writeMultipleRegisters(ubyte dev, ushort addr, ushort[] values) 150 { 151 enforce(values.length <= 125, "very big count"); 152 this.write(dev, 16, addr, cast(ushort)values.length, 153 cast(byte)(values.length*2), values); 154 this.read(4, dev, 16); 155 } 156 }