def read_float(self, file_table=8, start=0, total_float=1) -> list(): """ Read data from floats file table starting with 'start' offset :param file_table: file table used for register, default =8 :param start: starting address, default =0 :param total_float: total of floats (32 bits) to read :return: list of values if success, otherwise return [] or raise exception ; .data => list of words/status of bits if success or raise exception in case of error """ self.read_ok = False command = self.create_command(Command0FA2, bytes_to_read=total_float * 4, table=file_table, file_type=FileType.FLOAT, start=start, start_sub=0x00) try: reply = self.send_command(command) except Exception as e: print('[ERROR] Read float error',e) raise SendReceiveError() values = reply.get_data(FileType.FLOAT) self.read_ok = len(values) > 0 and command.tns == reply.tns if self.read_ok: self.data = values else: self.data =[] return self.data
def read_binary(self, file_table=3, start=0, bit=BIT.ALL, total_int=1) -> list(): """ Read data from binary file table starting with 'start' offset :param file_table: file table used for binary, default =3 :param start: starting address, default =0 :param bit: define which specific bit or all bits want to read from words, e.g: BIT.BIT0, BIT.ALL :param total_int: total of words (16 bits) to read :return: true if success, otherwise false ; .data => list of words/status of bits if success or raise exception in case of error """ self.read_ok = False command = self.create_command(Command0FA2, bytes_to_read=total_int * 2, table=file_table, file_type=FileType.BIT, start=start, start_sub=0x00) try: reply = self.send_command(command) except Exception as e: print('[ERROR] Read binary error',e) raise SendReceiveError() # Parsing based on the Category values = list() if bit == BIT.ALL: # return all integers as shown values = reply.get_data(FileType.INTEGER) else: # related individual bits for data in reply.get_data(FileType.INTEGER): status = data >> bit.value & 1 values.append(status) self.read_ok = len(values) > 0 and command.tns == reply.tns if self.read_ok: self.data = values else: self.data = [] return self.data
def read_counter(self, file_table=5, start=0, category=COUNTER.ACC, total_int=1) -> list(): """ Read data from counters file table starting with 'start' offset :param file_table: file table used for counter, default =5 :param start: starting address, default =0 :param category: define category to get, e.g: COUNTER.CU,.CD,.DN,.OV,.UN,.UA,.PRE,.ACC,.STATUS (entire word) :param total_int: total of words (16 bits) to read :return: list of values if success, otherwise return [] or raise exception ; .data => list of words/status of bits if success or raise exception in case of error """ self.read_ok = False # Based on category determine the Sub sub = 0 # are bits (CU,CD,DN,OV,UN,UA) or all of them in Status if category == COUNTER.PRE: sub = 1 elif category == COUNTER.ACC: sub = 2 command = self.create_command(Command0FA2, bytes_to_read=total_int * 2, table=file_table, file_type=FileType.COUNTER, start=start, start_sub=sub) try: reply = self.send_command(command) except Exception as e: print('[ERROR] Read counter error',e) raise SendReceiveError() # Parsing based on the Category values = list() if category in {COUNTER.PRE, COUNTER.ACC}: # return all integers as shown values = reply.get_data(FileType.INTEGER) else: # related with status or individual bits for data in reply.get_data(FileType.INTEGER): status = data >> 10 # the result of this are 6 bits on right side EN TI DN XX if category == COUNTER.CU: status = status >> 5 & 1 elif category == COUNTER.CD: status = status >> 4 & 1 elif category == COUNTER.DN: status = status >> 3 & 1 elif category == COUNTER.OV: status = status >> 2 & 1 elif category == COUNTER.UN: status = status >> 1 & 1 elif category == COUNTER.UA: status = status & 1 values.append(status) self.read_ok = len(values) > 0 and command.tns == reply.tns if self.read_ok: self.data = values else: self.data =[] return self.data
def send_command(self, command): """ Send the command, created by create_command :param command: created by created_command :return: list of values or raise exception in case of error """ """Doc page 4-6 Transmitter""" # print('Sending Command') for __ in range(3): # 3 # print('retry') # wait for any pending command to avoid conflict with other previous commands # make sure only one command is processing at a time self.wait_no_pending_command() # While clear any communication wth PLC hold any send command self.wait_while_com_clear() self.comm_history.append({'direction': 'out', 'command': command}) self._command_sent = command # record the command sent to compare with the tns of the message received self._plc.send_bytes(command.get_bytes()) retry_send = False got_ack = False i = 0 while i < 3: # 3 reply = self._expect_message() # print('reply',reply, type(reply)) if type(reply) is ReplyAck: got_ack = True # self._send_ack() # Added - Send Ack to PLC on time, this allow PLC knows we received the data i = 0 elif type(reply) is ReplyNak: command.tns = self._get_new_tns() retry_send = True elif type(reply) is ReplyTimeout or not reply.is_valid(): print('[ERROR] Error send command', reply) if got_ack: self._send_nak() else: self._send_enq() break # exit retry loop elif got_ack: # validate if this reply correspond to the command using transaction number (TNS), # otherwise drop it and keep trying if self._command_sent.tns == reply.tns: # Important to check the transaction #, otherwise drop it return reply else: # This could happened or either bad response from PLC or something happened with the queue # drop this message and Starting all over again self._messages_dropped += 1 print(f'[ERROR]**** Message dropped- CMD TNS:{self._command_sent.tns} Reply TNS:{reply.tns} ') # try one more time got_ack = False i=0 i += 1 if self._seq_sleep_time>0: time.sleep(self._seq_sleep_time) if not retry_send: self._plc.clear_buffer() # try to clear any buffer for retrying raise SendReceiveError() raise SendReceiveError()