1 /// 2 module modbus.backend.base; 3 4 import std.traits : Unqual, isNumeric, isArray; 5 import std.range : ElementType; 6 7 import modbus.types; 8 public import modbus.exception; 9 public import modbus.backend.specrules; 10 11 /// Message builder and parser 12 interface Backend 13 { 14 @nogc: 15 /++ Full build message for sending 16 17 work on preallocated buffer 18 19 Params: 20 buf = preallocated buffer 21 dev = modbus device number 22 fnc = function number 23 args = message data 24 25 Returns: 26 slice of preallocated buffer with message 27 +/ 28 void[] buildMessage(Args...)(void[] buf, ulong dev, ubyte fnc, ulong stamp, Args args) 29 { 30 size_t idx; 31 startMessage(buf, idx, dev, fnc, stamp); 32 recursiveAppend(buf, idx, args); 33 finalizeMessage(buf, idx); 34 return buf[0..idx]; 35 } 36 37 /// 38 void recursiveAppend(Args...)(void[] buf, ref size_t idx, Args args) 39 if (Args.length >= 1) 40 { 41 static if (Args.length == 1) 42 { 43 alias T = Args[0]; 44 auto val = args[0]; 45 static if (isArray!T) 46 { 47 static if (is(Unqual!(ElementType!T) == void)) 48 appendBytes(buf, idx, val); 49 else foreach (e; val) recursiveAppend(buf, idx, e); 50 } 51 else 52 { 53 static if (is(T == struct)) 54 foreach (v; val.tupleof) recursiveAppend(buf, idx, v); 55 else static if (isNumeric!T) append(buf, idx, val); 56 else static assert(0, "unsupported type " ~ T.stringof); 57 } 58 } 59 else static foreach (arg; args) recursiveAppend(buf, idx, arg); 60 } 61 62 /// 63 enum ParseResult 64 { 65 success, /// 66 incomplete, /// 67 checkFails /// 68 } 69 70 /++ Read data to temp message buffer 71 72 Params: 73 data = parsing data buffer, CRC and etc 74 result = reference to result message 75 +/ 76 ParseResult parseMessage(const(void)[] data, ref Message result); 77 78 /// 79 size_t aduLength(size_t dataBytes=0); 80 81 const(void)[] packT(T)(T value) { return sr.packT(value); } 82 T unpackT(T)(const(void)[] data) { return sr.unpackT!T(data); } 83 T unpackTT(T)(ref const T value) { return sr.unpackT!T((cast(void*)&value)[0..T.sizeof]); } 84 85 protected: 86 87 SpecRules sr() @property; 88 89 /// start building message 90 void startMessage(void[] buf, ref size_t idx, ulong dev, ubyte fnc, ulong stamp); 91 92 /// append data to message buffer 93 void append(T)(void[] buf, ref size_t idx, T val) 94 if (isNumeric!T && !is(T == real)) 95 { 96 union cst { T value; void[T.sizeof] data; } 97 appendBytes(buf, idx, sr.pack(cst(val).data[])); 98 } 99 /// ditto 100 void appendBytes(void[] buf, ref size_t idx, const(void)[]); 101 /// 102 void finalizeMessage(void[] buf, ref size_t idx); 103 } 104 105 /++ Basic functionality of Backend 106 +/ 107 abstract class BaseBackend : Backend 108 { 109 protected: 110 enum functionTypeSize = 1; 111 SpecRules specRules; 112 113 immutable size_t devOffset; 114 immutable size_t serviceData; 115 116 override SpecRules sr() @nogc @property { return specRules; } 117 118 public: 119 120 /++ 121 Params: 122 s = rules for pack N-byte data to sending package 123 serviceData = size of CRC for RTU, protocol id for TCP etc 124 deviceOffset = offset of device number (address) in message 125 +/ 126 this(SpecRules s, size_t serviceData, size_t deviceOffset) 127 { 128 import std.exception : enforce; 129 this.specRules = s !is null ? s : new BasicSpecRules; 130 this.serviceData = serviceData; 131 this.devOffset = deviceOffset; 132 } 133 134 @nogc: 135 136 override 137 { 138 ParseResult parseMessage(const(void)[] data, ref Message msg) 139 { 140 if (data.length < aduLength) 141 return ParseResult.incomplete; 142 if (auto err = sr.unpackDF(data[devOffset..$], msg.dev, msg.fnc)) 143 return ParseResult.incomplete; 144 if (!check(data)) 145 return ParseResult.checkFails; 146 147 msg.stamp = getStamp(data); 148 149 msg.data = data[startDataSplit..$-endDataSplit]; 150 return ParseResult.success; 151 } 152 153 size_t aduLength(size_t dataBytes=0) 154 { 155 return serviceData + 156 sr.deviceTypeSize + 157 functionTypeSize + 158 dataBytes; 159 } 160 } 161 162 protected: 163 164 override 165 { 166 void appendBytes(void[] buf, ref size_t idx, const(void)[] v) 167 { 168 auto inc = v.length; 169 if (idx + inc + serviceData >= buf.length) 170 throwModbusException("many args"); 171 buf[idx..idx+inc] = v[]; 172 idx += inc; 173 version (modbus_verbose) debug 174 .trace("append msg buffer data: ", buf[0..idx]); 175 } 176 } 177 178 abstract 179 { 180 void startMessage(void[] buf, ref size_t idx, ulong dev, ubyte func, ulong stamp); 181 void finalizeMessage(void[] buf, ref size_t idx); 182 183 bool check(const(void)[] data); 184 ulong getStamp(const(void)[] data); 185 size_t endDataSplit() @property; 186 } 187 188 size_t startDataSplit() @property 189 { return devOffset + sr.deviceTypeSize + functionTypeSize; } 190 191 void appendDF(void[] buf, ref size_t idx, ulong dev, ubyte fnc) 192 { appendBytes(buf, idx, sr.packDF(dev, fnc)); } 193 }