1 /** 2 * Communication channels 3 * 4 * Copyright: © 2016-2026 Orfeo Da Vià. 5 * License: Boost Software License - Version 1.0 - August 17th, 2003 6 * Authors: Orfeo Da Vià 7 */ 8 module dfins.channel; 9 10 import std.experimental.logger; 11 import core.time : dur; 12 import std.socket; 13 14 /** 15 * Timeout exception 16 */ 17 class ChannelTimeoutException : Exception { 18 this(string msg, string ip, const(ubyte[]) messageSent, string file = null, size_t line = 0) @trusted { 19 _ip = ip; 20 _messageSent = messageSent; 21 22 import std.string : format; 23 24 super("%s -- PLC ip %s".format(msg, ip)); 25 } 26 27 private string _ip; 28 string ip() { 29 return _ip; 30 } 31 32 private const(ubyte[]) _messageSent; 33 const(ubyte[]) messageSent() { 34 return _messageSent; 35 } 36 } 37 38 unittest { 39 ChannelTimeoutException ex = new ChannelTimeoutException("message", "192.168.221.1", [10, 11]); 40 assert(ex.ip == "192.168.221.1"); 41 assert(ex.messageSent == [10, 11]); 42 assert(ex.msg == "message -- PLC ip 192.168.221.1"); 43 } 44 45 46 interface IChannel { 47 ubyte[] send(const(ubyte[]) msg); 48 } 49 50 /** 51 * UDP channel. 52 */ 53 class UdpChannel : IChannel { 54 private Socket socket; 55 private Address address; 56 this(Socket socket, Address address) { 57 assert(socket !is null); 58 this.socket = socket; 59 60 assert(address !is null); 61 this.address = address; 62 } 63 64 /** 65 * Send a message and wait for a reply 66 */ 67 ubyte[] send(const(ubyte[]) msg) { 68 int attempt; 69 while (true) { 70 try { 71 return sendSingle(msg); 72 } catch (Exception e) { 73 ++attempt; 74 tracef("attempt %d failed", attempt); 75 if (attempt > 2) { 76 // tre tentativi 77 throw e; 78 } 79 } 80 } 81 } 82 83 private ubyte[] sendSingle(const(ubyte[]) msg) { 84 socket.sendTo(msg, address); 85 86 ubyte[1024] reply; 87 version (Win32) { 88 uint len = socket.receiveFrom(reply, address); 89 } else { 90 long len = socket.receiveFrom(reply, address); 91 } 92 93 if (len > 0) { 94 return reply[0 .. len].dup; 95 } else { 96 throw new ChannelTimeoutException("Channel send error", address.toAddrString, msg); 97 } 98 } 99 } 100 101 /** 102 * Convenience functions that create an `IChannel` object 103 * 104 * Params: 105 * ip = IP address 106 * timeout = Send and recieve timeout in ms 107 * port = Port number (defaul 9600) 108 */ 109 IChannel createUdpChannel(string ip, long timeout, ushort port = 9600) 110 in { 111 assert(ip.length); 112 assert(timeout >= 0); 113 } 114 do { 115 auto sock = new UdpSocket(); 116 sock.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(timeout)); 117 118 Address addr = parseAddress(ip, port); 119 return new UdpChannel(sock, addr); 120 }