sách gpt4 ăn đã đi

Phân tích mã nguồn thư viện Pythonmodbus_tk

In lại Tác giả: Tôi là chú chim nhỏ Thời gian cập nhật: 2023-06-11 22:33:44 24 4
mua khóa gpt4 giày nike

phân tích mã nguồn modbus_tk

Lời nói đầu

Giao thức modbus_tcp là giao thức trao đổi dữ liệu thiết bị phổ biến dựa trên giao thức TCP/IP trong các dự án công nghiệp.

Là giao thức lớp trên của giao thức TCP/IP, giao thức modbus_tcp bao gồm hai khái niệm: máy khách và máy chủ. Nhưng tên chuẩn hơn nên là chủ và nô lệ.

  • Slave: phía máy chủ trong giao thức TCP/IP
  • Master: phía máy khách trong giao thức TCP/IP

Thư viện modbus_tk là một mô-đun đóng gói giao thức modbus nổi tiếng trong Python và mã nguồn của nó xứng đáng được nghiên cứu chuyên sâu.

Đặc biệt là khi có một số yêu cầu nhất định về tính đồng thời và các khía cạnh khác, nếu cần phát triển thêm dựa trên mô-đun modbus_tk thì mã nguồn và logic triển khai của nó phải được nghiên cứu cẩn thận.

Vì vậy, tôi viết bài viết này, hy vọng nó sẽ hữu ích cho bạn.

Khởi tạo đối tượng TcpMaster

Nhập lớp TcpMaster:

                        
                          từ modbus_tk.modbus_tcp nhập TcpMaster

                        
                      

TcpMaster kế thừa từ Master và không làm gì khi nó được khởi tạo.

                        
                          lớp TcpMaster(Master): def __init__(self, host="127.0.0.1", port=502, timeout_in_sec=5.0): super(TcpMaster, self).__init__(timeout_in_sec) self._host = host self._port = port self._sock = Không có

                        
                      

Phương thức __init__() của Master cũng không làm gì cả:

                        
                          lớp Master(object): def __init__(self, timeout_in_sec, hooks=None): self._timeout = timeout_in_sec self._verbose = False self._is_opened = False # Mô tả _is_opened Không có False

                        
                      

Thiết lập liên kết ổ cắm

Lớp cha Master của TcpMaster cung cấp phương thức thực thi, cung cấp các tham số sau

                        
                          self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="", expected_length=-1, write_starting_address_fc23=0, number_file=None, pdu="", returns_raw=False

                        
                      

Phương thức này về cơ bản là cốt lõi của mô-đun. Phương thức này được gọi cho dù đó là đọc và ghi cuộn dây hay đọc và ghi các thanh ghi.

Tiếp theo, chúng ta sẽ bắt đầu phân tích từng dòng cách triển khai cụ thể phần nội dung mã của nó:

                        
                          is_read_function = False nb_of_digits = 0 nếu number_file là Không có: number_file = tuple() self.open()

                        
                      

Giá trị được gán ở đây cho is_read_function là Sai, có nghĩa là trước khi phương thức Master.execute() thực sự được thực thi, trước tiên tác giả sẽ nghĩ rằng người dùng gọi phương thức ghi thay vì phương thức đọc.

Tiếp theo, phương thức self.open() được gọi trong mã. Vì không có gì được thực hiện khi khởi tạo lớp TcpMaster nên kết nối TCP vẫn chưa được thiết lập tại thời điểm này và phương thức self.open() là để tạo một máy khách TCP.

                        
                          def open(self): if not self._is_opened: # Trong phương thức khởi tạo, nó mặc định là False self._do_open() self._is_opened = True

                        
                      

Phương thức self._do_open() được thực thi ở đây được TcpMaster triển khai:

                        
                          def _do_open(self): if self._sock: # Nếu self._sock không phải là None, hãy đóng đối tượng socket self._sock.close() # Tạo một đối tượng socket, AF_INET là họ địa chỉ IPV4 # SOCK_STREAM là một đối tượng dựa trên luồng Protocol , nghĩa là giao thức TCP self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Đặt thời gian chờ, là giá trị được truyền vào khi khởi tạo TcpMaster. Tham số mặc định là 5 self.set_timeout(self.get_timeout()) # Cho phép sử dụng lại địa chỉ (giải quyết vấn đề chiếm cổng) self._sock.setsockopt (socket. SOL_SOCKET, socket.SO_REUSEADDR, 1) call_hooks("modbus_tcp.TcpMaster.b Before_connect", (self, )) # Kết nối self._sock.connect((self._host, self._port)) call_hooks("modbus_tcp.TcpMaster.after_connect", (self, ))

                        
                      

self.set_timeout ở đây được TcpMaster triển khai

                        
                          def set_timeout(self, timeout_in_sec): super(TcpMaster, self).set_timeout(timeout_in_sec) if self._sock: # Lưu ý! Nếu timeout_in_sec bằng 0 # thì đối tượng sock là một đối tượng không chặn thời gian liên kết # và có thể được sử dụng cho Ghép kênh I/O self._sock.setblocking(timeout_in_sec > 0) # if timeout_in_sec Nếu là 0, nó được đặt thành đối tượng ổ cắm bị chặn # thời gian chờ không được truyền số âm nếu timeout_in_sec: self._sock.settimeout(timeout_in_sec)

                        
                      

Nhìn thấy điều này, chúng ta không khó để đoán rằng phương thức Master.execute() của TcpMaster trong module modbus_tk thực sự có thể hỗ trợ kết nối lại một cách vô nghĩa sau một ngoại lệ self._sock.

Bạn chỉ cần gọi lại phương thức TcpMaster._do_open() sau khi nô lệ mất liên lạc để đạt được kết nối lại không thể nhận thấy.

Ghi vào nhiều thanh ghi

Tiếp theo, phương thức Master.execute() về cơ bản là mã giải nén và đóng gói cho giao thức TCP, trước tiên tôi đã nhận xét về cách đóng gói cụ thể và các mã quy trình khác.

                        
                          @threadsafe_function def execute(self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="", expected_length=-1, write_starting_address_fc23=0, number_file=None, pdu="", returns_raw=False ): is_read_function = False nb_of_digits = 0 nếu number_file là None: number_file = tuple() self.open() nếu function_code == định nghĩa.READ_COILS hoặc function_code == định nghĩa.READ_DISCRETE_INPUTS: truyền elif function_code == định nghĩa.READ_INPUT_REGISTERS hoặc function_code == định nghĩa.READ_HOLDING_REGISTERS: truyền elif function_code == định nghĩa.READ_FILE_RECORD: truyền elif (function_code == định nghĩa.WRITE_SINGLE_COIL) hoặc (function_code == định nghĩa.WRITE_SINGLE_REGISTER): truyền elif function_code == định nghĩa.WRITE_MULTIPLE_COILS: truyền elif function_code == định nghĩa.WRITE_MULTIPLE_REGISTERS: truyền elif function_code == định nghĩa.READ_EXCEPTION_STATUS: truyền elif function_code == định nghĩa.DIAGNOSTIC: truyền elif function_code == định nghĩa.READ_WRITE_MULTIPLE_REGISTERS: truyền elif function_code == định nghĩa.RAW: truyền elif function_code == định nghĩa.DEVICE_INFO: truyền else: raise ModbusFunctionNotSupportedError("Mã hàm {0} không được hỗ trợ. ".format(function_code)) query = self._make_query() request = query.build_request(pdu, slave) retval = call_hooks("modbus.Master.before_send", (self, request)) nếu retval không phải là None: request = retval nếu self._verbose: LOGGER.debug(get_log_buffer("-> ", request)) self._send(request) call_hooks("modbus.Master.after_send", (self, )) nếu slave != 0: pass

                        
                      

Để tiếp tục phân tích, chúng ta sẽ bắt đầu với logic ghi nhiều thanh ghi rồi nhìn xuống:

                        
                          ĐỌC_GHI_NHIỀU_ĐĂNG_KÝ

                        
                      

Mã của nó là

                        
                          elif function_code == định nghĩa.WRITE_MULTIPLE_REGISTERS: # Giá trị và định dạng đầu ra. Nếu định dạng và giá trị đầu ra được chỉ định, logic sau sẽ được chạy nếu out_value và data_format: byte_count = struct.calcsize(data_format) # Ngược lại, hãy tính độ dài của toàn bộ byte đầu tiên khác: byte_count = 2 * len(output_value) # Sử dụng struct để chuyển đổi dữ liệu, sử dụng cách sắp xếp big-endian pdu = struct.pack(">BHHB", function_code, started_address, byte_count // 2, byte_count) # Giá trị và định dạng đầu ra. Nếu định dạng và giá trị đầu ra được chỉ định, logic sau sẽ được chạy nếu out_value và data_format: pdu += struct. pack (data_format, *output_value) # Nói chung, chúng tôi sẽ không chỉ định data_format, vì vậy chỉ cần nhìn xuống phần khác: for j in out_value: # Nếu j lớn hơn 0 fmt là H, ngược lại là h fmt = "H" if j >= 0 else "h" # Tiếp tục thêm dữ liệu vào pdu pdu += struct.pack(">" + fmt, j) data_format = ">HH" nếu dự kiến_length < 0: dự kiến_length = 8

                        
                      

Xây dựng gói dữ liệu

Dù đọc hay ghi cuộn dây hay thanh ghi, mỗi yêu cầu sẽ chứa một đơn vị dữ liệu pdu.

Theo phương thức Master.execute(), mỗi thao tác đọc và ghi sẽ chạy phương thức TcpMaster._make_query()

                        
                          truy vấn = self._make_query() yêu cầu = truy vấn.build_request(pdu, slave)

                        
                      

Sau đây là mã của TcpMaster._make_query

                        
                          def _make_query(self): trả về TcpQuery()

                        
                      

TcpQuery là một lớp dẫn xuất của Truy vấn, nhưng Truy vấn thực sự là một lớp giao diện, do đó không có mã thực tế

                        
                          class TcpQuery(Query): _last_transaction_id = 0 # Ghi nhớ thuộc tính lớp này def __init__(self): super(TcpQuery, self).__init__() self._request_mbap = TcpMbap() self._response_mbap = TcpMbap()

                        
                      

Quá trình khởi tạo của lớp TcpMbap cũng rất đơn giản, thực tế là gói gọn một yêu cầu và phản hồi:

                        
                          lớp TcpMbap(đối tượng): def __init__(self): self.transaction_id = 0 self.protocol_id = 0 self.length = 0 self.unit_id = 0

                        
                      

Triển khai TcpQuery.build_request()

                        
                          def build_request(self, pdu, Slave): if (nô lệ < 0) hoặc (nô lệ > 255): raise InvalidArgumentError("{0} Giá trị không hợp lệ cho id nô lệ".format(slave)) self._request_mbap.length = len( pdu) + 1 # Độ dài của đơn vị dữ liệu pdu + 1 self._request_mbap.transaction_id = self._get_transaction_id() # Nhận id giao dịch self._request_mbap.unit_id = nô lệ # Số trạm mbap = self._request_mbap.pack() # Gói # đơn vị dữ liệu mbap và pdu được ghép và trả về # mbap có thể được coi là đầu và pdu là body return mbap + pdu

                        
                      

TcpQuery._get_transaction_id() sẽ tăng số lượng giao dịch lên 1 mỗi lần gửi và nhận gói. Khi số giao dịch tăng lên 65535, nó sẽ được đặt thành 0:

                        
                          @threadsafe_function def _get_transaction_id(self): nếu TcpQuery._last_transaction_id < 0xffff: # 65535 TcpQuery._last_transaction_id += 1 nếu không: TcpQuery._last_transaction_id = 0 trả về TcpQuery._last_transaction_id

                        
                      

Phương thức TcpMbap.pack() sẽ đóng gói tất cả các thuộc tính instance trong TcpMbap.__init__() thông qua struct

                        
                          gói def (tự): # số giao dịch giao dịch_id # giao thức_id 0 # độ dài pdu độ dài đơn vị dữ liệu + 1 # unit_id số trạm thiết bị nô lệ return struct.pack(">HHHB", self.transaction_id, self.protocol_id, self.length, self .unit_id)

                        
                      

Tại thời điểm này, yêu cầu yêu cầu đã được xây dựng.

Gửi yêu cầu

Hãy quay lại phương thức Master.execute()

                        
                          # call_hooks thực sự chạy hàm hook, chức năng này sẽ được mô tả chi tiết sau retval = call_hooks("modbus.Master.Before_send", (self, request)) nếu retval không phải là None: request = retval # Có cần in thêm nhật ký hay không ? Điều này có thể được thiết lập thông qua phương thức Master.set_verbose() # Giá trị mặc định của nó là Sai nếu self._verbose: LOGGER.debug(get_log_buffer("-> ", request)) # Gửi yêu cầu self._send(request) call_hooks(" modbus.Master.after_send", (tự, ))

                        
                      

Trong phương thức TcpMaster._send():

                        
                          def _send(self, request): retval = call_hooks("modbus_tcp.TcpMaster.Before_send", (self, request)) if retval không phải None: request = retval thử: # Làm mới socket để đảm bảo liên kết có sẵn ._sock, 3 ) ngoại trừ Ngoại lệ dưới dạng thông báo: LOGGER.error('Error while cleaning the socket: {0}'.format(msg)) # Sau ngoại lệ, TcpMaster._do_open() sẽ được chạy lại để cố gắng kết nối lại self._do_open() # Nếu hàmflush_socket() chạy mà không đưa ra ngoại lệ, điều đó có nghĩa là liên kết có sẵn. # Chỉ khi đó dữ liệu mới được gửi self._sock.send(request)

                        
                      

Hàm Flush_socket() rất thú vị. Nó liên tục thăm dò và theo dõi trạng thái có thể đọc được của đối tượng sock thông qua mô-đun chọn. Khi có thể đọc được, nó sẽ tự động đọc 1024 byte dữ liệu mỗi lần và loại bỏ chúng. Thao tác một bước để kiểm tra xem không có bất thường nào về trạng thái kết nối trước dữ liệu:

                        
                          def Flush_socket(socks, lim=0): # lim được truyền vào 3, nghĩa là nó có thể được đọc tối đa 3 lần input_socks = [socks] # Tạo một danh sách nghe cnt = 0 # Số lần đọc hiện tại trong khi True: # Đặt danh sách sự kiện có thể đọc, danh sách sự kiện có thể ghi, danh sách sự kiện lỗi và đối tượng nghe # Nó sẽ trả về một danh sách: # [[r_fd, r_fd], [w_fd, w_fd], [e_fd, e_fd]] # Và [0] có nghĩa là chỉ lấy danh sách mô tả tệp có thể đọc được # Thời gian sự kiện vòng lặp được đặt thành 0,0, có nghĩa là nó sẽ chặn ở đây cho đến khi sự kiện fd được kích hoạt # Nếu không phải là 0 , đợi n giây và thực hiện chu kỳ tiếp theo i_socks = select.select(input_socks, input_socks, input_socks, 0.0)[0] # Nếu không có bộ mô tả tập tin có thể đọc được, hãy nhảy ra khỏi vòng lặp while if len(i_socks) == 0: break # Nếu bạn hiểu được nó, hãy lặp lại để lấy tất cho recv # Trên thực tế, điều này cũng nên được viết là i_socks[0 ].recv(1024) # Bởi vì bộ mô tả tệp sự kiện có thể đọc được cho sock trong i_socks: sock.recv(1024) # vượt quá giới hạn đọc tối đa, Điều này thể hiện rằng kết nối bị ngắt kết nối nếu lim > 0: cnt += 1 if cnt >= lim: raise Ngoại lệ("flush_socket: đạt đến số lần lặp tối đa")


                        
                      

Tại thời điểm này, chúng tôi đã hoàn thành phân tích mã nguồn hoàn chỉnh về việc đóng gói và gửi dữ liệu.

Phân tích phản hồi

Tiếp theo chúng ta hãy xem mã để phân tích thông tin phản hồi trong phương thức Master.execute():

                        
                          nếu slave != 0: phản hồi = self._recv(expected_length) vượt qua

                        
                      

Đầu tiên, nếu số trạm không bằng 0, phương thức TcpMaster._recv() sẽ được thực thi:

                        
                          def _recv(self, Expected_length=-1): # Hàm to_data sẽ trả về các nội dung khác nhau tùy theo phiên bản Python # Nếu là Python2, nó sẽ trả về trực tiếp chuỗi '' # Nếu là Python3, nó sẽ trả về một bytearray(' ', 'ascii') phản hồi = to_data('') length = 255 # Nếu phản hồi nhỏ hơn 255, hãy đọc liên tục trong khi len(response) < length: rcv_byte = self._sock.recv(1) if rcv_byte: reply += rcv_byte # Giải nén thông qua struct.unpack() ở byte thứ 6 if len(response) == 6: to_be_recv_length = struct.unpack(" >HHH", reply) [2] chiều dài = to_be_recv_length + 6 khác: break retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, reply)) nếu retval không phải None: return retval return reply

                        
                      

Sau khi nhận được phản hồi, phương thức Master.execute() sẽ bắt đầu phân tích thông tin phản hồi

                        
                          retval = call_hooks("modbus.Master.after_recv", (self, response)) nếu retval không phải là None: response = retval nếu self._verbose: LOGGER.debug(get_log_buffer("<- ", response)) response_pdu = query.parse_response(response)

                        
                      

Mã của phương thức TcpQuery.parse_response() chủ yếu phân tách mbap và pdu, giải nén mbap thông qua phương thức TcpMbap.unpack() và thực hiện xác minh dữ liệu thông qua TcpMbap.check_response()

                        
                          def parse_response(self, reply): if len(response) > 6: # Lấy mbap và pdu mbap tương ứng, pdu = reply[:7], reply[7:] # Giải nén self._response_mbap.unpack(mbap) # Dữ liệu xác minh , mbap và pdu được yêu cầu gửi đến Độ dài của self._response_mbap.check_response(self._request_mbap, len(pdu)) # Return pdu return pdu else: raise ModbusInvalidResponseError("Độ dài phản hồi chỉ là {0} byte. ".format(len(response)))

                        
                      

Mã phương thức TcpMbap.unpack() như sau, cập nhật id giao thức số giao dịch và các thông tin khác của _response_mbap

                        
                          def unpack(self, giá trị): (self.transaction_id, self.protocol_id, self.length, self.unit_id) = struct.unpack(">HHHB", giá trị)

                        
                      

Mã phương thức TcpMbap.check_response() như sau

                        
                          def check_response(self, request_mbap, response_pdu_length): error_str = self._check_ids(request_mbap) error_str += self.check_length(response_pdu_length) nếu len(error_str) > 0: tăng ModbusInvalidMbapError(error_str)

                        
                      

Mã phương thức TcpMbap._check_ids() như sau

                        
                              def _check_ids(self, request_mbap): # self là nội dung phản hồi, request_mbap là nội dung yêu cầu # So sánh số giao dịch của họ và thông tin khác để xem chúng có nhất quán hay không, error_str sẽ được trả về và sẽ được kiểm tra. trong TcpMbap.check_response() # raise error_str = "" if request_mbap.transaction_id != self.transaction_id: error_str += "Giao dịch không hợp lệ id: request={0} - reply={1}. ".format( request_mbap.transaction_id, self.transaction_id) if request_mbap.protocol_id != self.protocol_id: error_str += "Id giao thức không hợp lệ: request={0} - phản hồi={1}. ".format( request_mbap.protocol_id, self.protocol_id ) nếu request_mbap.unit_id != self.unit_id: error_str += "Id đơn vị không hợp lệ: request={0} - reply={1}. ".format(request_mbap.unit_id, self.unit_id) trả về error_str

                        
                      

Mã phương thức TcpMbap.check_length() như sau

                        
                          def check_length(self, pdu_length): # Nghĩ xem tại sao độ dài pdu lại là +1? # Bởi vì phản hồi nằm trong phương thức TcpMbap.unpack() nên self.length là độ dài của mbap + pdu # Vì vậy pdu_length length + 1 ở đây thực chất đề cập đến độ dài của toàn bộ phần đầu + phần thân theo sau_bytes_length = pdu_length+1 # Xác định xem độ dài bằng nhau. Nếu lý do có thể không chờ đợi là dữ liệu được giải nén không chính xác, mbap quá dài hoặc pdu quá ngắn # Trong trường hợp này, một chuỗi sẽ được trả về trực tiếp #. Trong TcpMbap.check_response(), nếu độ dài của error_str lớn hơn 0, một ngoại lệ sẽ được đưa ra nếu self.length !=follow_bytes_length: return "Độ dài phản hồi là {0} trong khi nhận được {1} byte. ".format(self .length, follow_bytes_length) trả về ""

                        
                      

Tại thời điểm này, phương thức TcpQuery().parse_response() đã được thực thi hoàn toàn.

Đơn vị dữ liệu pdu thu được trong phương thức Master.execute(). Đó là, toàn bộ cơ thể dữ liệu.

Hãy chuyển sang phương thức Master.execute() Trên thực tế, không có lệnh gọi sâu nào tới một số mã nội bộ và không có thao tác I/O mới nào:

                        
                          
reply_pdu = query.parse_response(response) (return_code, byte_2) = struct.unpack(">BB", reply_pdu[0:2]) # Nếu mã trả về lớn hơn 128, lỗi sẽ được báo cáo trực tiếp nếu return_code > 0x80 : # nô lệ đã trả về một lỗi ngoại lệ_code = byte_2 raise ModbusError(Exception_code) else: # Phần sau được phân tích cú pháp phần nội dung và data_format # Chúng là các thao tác đọc, thông tin thiết bị và ghi không hợp lệ raise ModbusInvalidResponseError( "Số byte là {0} trong khi số byte thực tế là {1}. ".format(byte_count, len(data)) ) elif function_code == định nghĩa.DEVICE_INFO: data = reply_pdu[1:] data_format = ">" + (len(data) * "B") else: # trả về những gì được Slave trả về sau khi ghi hàm data = reply_pdu[ 1:] # Mặc định là Sai nếu return_raw: trả về dữ liệu # Giải nén, định dạng dữ liệu và dữ liệu thu được bằng cách đọc, ghi và thông tin thiết bị # Vận hành kết quả dữ liệu = struct.unpack(data_format, data) # Chỉ khi function_code là READ_COILS, nb_of_digits khác 0 nếu nb_of_digits > 0: chữ số = [] for byte_val in result: for i in range(8): if len(digits) >= nb_of_digits : ngắt chữ số.append(byte_val % 2) byte_val = byte_val >> 1 result = tuple(digits) # Nếu function_code là READ_FILE_RECORD để đọc bản ghi file, kết quả cũng cần được # sửa đổi lại nếu function_code == định nghĩa.READ_FILE_RECORD: sub_seq = list() ptr = 0 while ptr < len( result ): sub_seq += ((ptr + 2, ptr + 2 + result[ptr] // 2), ) ptr += result[ptr] // 2 + 2 result = tuple(map(lambda sub_seq_x: result[sub_seq_x[0]:sub_seq_x[1]], sub_seq)) # Trả về kết quả trả về kết quả

                        
                      

công cụ trang trí threadsafe

threadsafe là một hàm trang trí. Trình trang trí này được thêm vào tiêu đề của phương thức Master.execute() và phương thức TcpQuery._get_transaction_id().

Đúng như tên gọi, mục đích chính của trình trang trí này là đảm bảo an toàn cho luồng (một số thiết bị có thể không hỗ trợ các thao tác đọc và ghi đồng thời), nhưng trình trang trí này cũng có thể gây ra các vấn đề khác.

Trước tiên hãy xem mã nguồn của nó:

                        
                          def threadsafe_function(fcn): # Khởi tạo một khóa đệ quy lock = threading.RLock() def new(*args, **kwargs): # Khi các phương thức Master.execute() và TcpQuery._get_transaction_id() không thành công# Khi đối số từ khóa được chuyển tới threadsafe=False, chế độ thread-safe sẽ được bật theo mặc định để thực thi #This two Method threadsafe = kwargs.pop('threadsafe', Đúng) nếu threadsafe: lock.acquire() thử: ret = fcn(*args, **kwargs) ngoại trừ Ngoại lệ là excpt: raise excpt cuối cùng: if threadsafe: lock.release() return ret return new

                        
                      

Khóa luồng này sẽ gây ra vấn đề gì? Trình trang trí này sẽ được thực thi tự động khi trình thông dịch Python chạy tới phương thức Master.execute().

Biến khóa được tạo và cuối cùng hàm đóng bên trong new() được trả về.

Có thể hiểu rằng khóa này đã được coi là một biến toàn cục cho dù có bao nhiêu đối tượng thể hiện của TcpMaster được tạo sau đó thì khóa được trỏ đến bởi biến khóa sẽ giống nhau.

Thông qua phân tích mã nguồn, chúng tôi biết rằng phương thức Master.execute() sẽ thiết lập một liên kết ổ cắm. Khi một liên kết thiết bị mất quá nhiều thời gian, nó cũng sẽ khiến các liên kết hoặc liên lạc của thiết bị khác bị chặn.

Bởi vì tất cả đều sử dụng cùng một khóa. Do đó, nói chung, khi sử dụng nó, chúng ta sẽ chuyển rõ ràng tham số từ khóa threadsafe=False trong phương thức Master.execute() và tự mình thực hiện khóa để giải quyết vấn đề mà cùng một thiết bị không thể đọc và ghi cùng một lúc.

Một số chức năng hook hook

Khi phân tích mã nguồn ở trên, chúng ta sẽ thấy hoạt động của nhiều call_hooks, thực chất là các hàm hook do module modbus_tk cung cấp. Miễn là chức năng hook tương ứng được triển khai, nó sẽ tự động chạy trong toàn bộ vòng đời truyền dữ liệu modbus_tcp.

Sau đây là các hàm hook phổ biến:

                        
                          def install_hook(name, fct): """ Cài đặt một trong các hook sau modbus_rtu.RtuMaster.before_open((master,)) modbus_rtu.RtuMaster.after_close((master,) modbus_rtu.RtuMaster.before_send((master, request)) trả về yêu cầu đã sửa đổi hoặc Không có modbus_rtu.RtuMaster.after_recv((master, response)) trả về phản hồi đã sửa đổi hoặc Không có modbus_rtu.RtuServer.before_close((server, )) modbus_rtu.RtuServer.after_close((server, )) modbus_rtu.RtuServer.before_open((server, )) modbus_rtu.RtuServer.after_open(((server, )) modbus_rtu.RtuServer.after_read((server, request)) trả về yêu cầu đã sửa đổi hoặc Không có modbus_rtu.RtuServer.before_write((server, response)) trả về phản hồi đã sửa đổi hoặc Không có modbus_rtu.RtuServer.after_write((máy chủ, phản hồi)) modbus_rtu.RtuServer.on_error((máy chủ, excpt)) modbus_tcp.TcpMaster.before_connect((chủ, )) modbus_tcp.TcpMaster.after_connect((chủ, )) modbus_tcp.TcpMaster.before_close((chủ, )) modbus_tcp.TcpMaster.after_close((chủ, )) modbus_tcp.TcpMaster.before_send((chủ, yêu cầu)) modbus_tcp.TcpServer.after_send((chủ, yêu cầu)) modbus_tcp.TcpMaster.after_recv((chủ, phản hồi)) modbus_tcp.TcpServer.on_connect((máy chủ, máy khách, địa chỉ)) modbus_tcp.TcpServer.on_disconnect((máy chủ, sock)) modbus_tcp.TcpServer.after_recv((máy chủ, sock, yêu cầu)) trả về yêu cầu đã sửa đổi hoặc Không có modbus_tcp.TcpServer.before_send((server, sock, response)) trả về phản hồi đã sửa đổi hoặc Không có modbus_tcp.TcpServer.on_error((server, sock, excpt)) modbus_rtu_over_tcp.RtuOverTcpMaster.after_recv((master, response)) modbus.Master.before_send((master, request)) trả về yêu cầu đã sửa đổi hoặc Không có modbus.Master.after_send((master)) modbus.Master.after_recv((master, response)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Slave.handle_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Slave.handle_write_multiple_coils_request((slave, request_pdu)) modbus.Slave.handle_write_multiple_registers_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Slave.handle_write_single_register_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_write_single_coil_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_input_registers_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_holding_registers_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_discrete_inputs_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_coils_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_write_multiple_registers_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không modbus.Slave.handle_read_exception_status_request((slave, request_pdu)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Slave.on_handle_broadcast((slave, response_pdu)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Slave.on_exception((slave, function_code, excpt)) modbus.Databank.on_error((db, excpt, request_pdu)) modbus.ModbusBlock.setitem((self, slice, value)) modbus.Server.before_handle_request((server, request)) trả về yêu cầu đã sửa đổi hoặc Không có modbus.Server.after_handle_request((server, response)) trả về phản hồi đã sửa đổi hoặc Không có modbus.Server.on_exception((server, excpt)) """ với _LOCK: thử: _HOOKS[name].append(fct) ngoại trừ KeyError: _HOOKS[name] = [fct]

                        
                      

Cuối cùng, bài viết về phân tích mã nguồn của thư viện Pythonmodbus_tk kết thúc tại đây. Nếu bạn muốn biết thêm về phân tích mã nguồn của thư viện Pythonmodbus_tk, vui lòng tìm kiếm các bài viết về CFSDN hoặc tiếp tục duyệt qua các bài viết liên quan. blog trong tương lai! .

24 4 0
tôi là một con chim nhỏ
Hồ sơ

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá taxi Didi miễn phí
Phiếu giảm giá taxi Didi
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress