Coverage for C:\Users\hjanssen\HOME\pyCharmProjects\ethz_hvl\hvl_ccb\hvl_ccb\comm\modbus_tcp.py: 43%

# Coverage for C:\Users\hjanssen\HOME\pyCharmProjects\ethz_hvl\hvl_ccb\hvl_ccb\comm\modbus_tcp.py : 43%

## 60 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# Copyright (c) 2019-2020 ETH Zurich, SIS ID and HVL D-ITET

2#

3"""

4 Communication protocol for modbus TCP ports. Makes use of the

5`pymodbus <https://pymodbus.readthedocs.io/en/latest/>`_ library.

6"""

7

8 import logging

9 from typing import List, Union

10

11 from IPy import IP

12 from pymodbus.client.sync import ModbusTcpClient

13 from pymodbus.constants import Defaults as ModbusDefaults

14 from pymodbus.exceptions import ConnectionException

15

16 from .base import CommunicationProtocol

17 from ..configuration import configdataclass

18

19

20 class ModbusTcpConnectionFailedException(ConnectionException):

21 """

22 Exception raised when the connection failed.

23 """

24 pass

25

26

27@configdataclass

28 class ModbusTcpCommunicationConfig:

29 """

30 Configuration dataclass for :class:`ModbusTcpCommunication`.

31 """

32

33 #: Host is the IP address of the connected device.

34 host: str

35

36 #: Unit number to be used when connecting with Modbus/TCP. Typically this is used

37 #: when connecting to a relay having Modbus/RTU-connected devices.

38 unit: int

39

40 #: TCP port

41 port: int = ModbusDefaults.Port

42

43 def clean_values(self):

44 # host, raises ValueError on its own if not suitable

45 IP(self.host)

46

47

48 class ModbusTcpCommunication(CommunicationProtocol):

49 """

50 Implements the Communication Protocol for modbus TCP.

51 """

52

53 def __init__(self, configuration):

54 """Constructor for modbus"""

55 super().__init__(configuration)

56

57 # create the modbus port specified in the configuration

58 logging.debug(

59 'Create ModbusTcpClient with host: "{}", Port: "{}", Unit: "{}"'.format(

60 self.config.host,

61 self.config.port,

62 self.config.unit,

63 )

64 )

65 self.client = ModbusTcpClient(

66 self.config.host, port=self.config.port, unit=self.config.unit

67 )

68

69 @staticmethod

70 def config_cls():

71 return ModbusTcpCommunicationConfig

72

73 def open(self) -> None:

74 """

75 Open the Modbus TCP connection.

76

77 :raises ModbusTcpConnectionFailedException: if the connection fails.

78 """

79

80 # open the port

81 logging.debug('Open Modbus TCP Port.')

82

83 with self.access_lock:

84 if not self.client.connect():

85 raise ModbusTcpConnectionFailedException

86

87 def close(self):

88 """

89 Close the Modbus TCP connection.

90 """

91

92 # close the port

93 logging.debug('Close Modbus TCP Port.')

94

95 with self.access_lock:

96 self.client.close()

97

98 def write_registers(self, address: int, values: Union[List[int], int]):

99 """

100 Write values from the specified address forward.

101

102 :param address: address of the first register

103 :param values: list with all values

104 """

105

106 logging.debug(

107 'Write registers {address} with values {values}'.format(

108 address=address,

109 values=values,

110 )

111 )

112

113 with self.access_lock:

114 try:

115 self.client.write_registers(address=address, values=values,

116 unit=self.config.unit)

117 except ConnectionException as e:

118 raise ModbusTcpConnectionFailedException from e

119

120 def read_holding_registers(self, address: int, count: int) -> List[int]:

121 """

122 Read specified number of register starting with given address and return

123 the values from each register.

124

125 :param address: address of the first register

126 :param count: count of registers to read

127 :return: list of `int` values

128 """

129

130 logging.debug(

131 'Read holding registers {address} with count {count}.'.format(

132 address=address,

133 count=count,

134 )

135 )

136

137 with self.access_lock:

138 try:

139 registers = self.client.read_holding_registers(

140 address=address,

141 count=count

142 ).registers

143 except ConnectionException as e:

144 raise ModbusTcpConnectionFailedException from e

145

146 logging.debug(

147 'Returned holding registers {address}: {registers}'.format(

148 address=address,

149 registers=registers,

150 )

151 )

152

153 return registers

154

155 def read_input_registers(self, address: int, count: int) -> List[int]:

156 """

157 Read specified number of register starting with given address and return

158 the values from each register in a list.

159

160 :param address: address of the first register

161 :param count: count of registers to read

162 :return: list of `int` values

163 """

164

165 logging.debug(

166 'Read input registers {address} with count {count}.'.format(

167 address=address,

168 count=count,

169 )

170 )

171

172 with self.access_lock:

173 try:

174 registers = self.client.read_input_registers(

175 address=address,

176 count=count

177 ).registers

178 except ConnectionException as e:

179 raise ModbusTcpConnectionFailedException from e

180

181 logging.debug(

182 'Returned input registers {address}: {registers}'.format(

183 address=address,

184 registers=registers,

185 )

186 )

187

188 return registers