def read_sensors(request): sensors = Sensor.objects.all() sensor_data = [] dictionary = {} for sensor in sensors: client = ModbusTcpClient(sensor.address.server, port=502, timeout=1) try: client.connect() if sensor.sensor_type == Sensor.SensorType.DIGITAL: reply = client.read_discrete_inputs(sensor.channel, 1, unit=1) sensor_reading = reply.bits[0] sensor_response = { 'sensor_name': sensor.name, 'sensor_reading': sensor_reading } if sensor.digital_sensor_alert and sensor.email and str( sensor_reading) != sensor.last_value: if sensor.digital_alert_value and sensor_reading: send_sensor_alert_email(sensor, str(sensor_reading)) #send_sensor_alert_email() elif not sensor.digital_alert_value and not sensor_reading: send_sensor_alert_email(sensor, str(sensor_reading)) #send_sensor_alert_email() else: reply = client.read_input_registers(sensor.channel, 1, unit=1) sensor_reading = round( reply.registers[0] * sensor.conversion_factor, 2) if sensor.high_alert_value and sensor.email: if sensor_reading > sensor.high_alert_value and float( sensor.last_value) <= sensor.high_alert_value: send_sensor_alert_email(sensor, str(sensor_reading)) #send_sensor_alert_email() if sensor.low_alert_value and sensor.email: if sensor_reading < sensor.low_alert_value and float( sensor.last_value) >= sensor.low_alert_value: send_sensor_alert_email(sensor, str(sensor_reading)) #send_sensor_alert_email() sensor.last_value = str(sensor_reading) sensor.save() client.close() except: sensor_reading = "Could Not Connect" sensor_response = { 'sensor_name': sensor.name, 'sensor_reading': sensor_reading } sensor_data.append(sensor_response) client.close() dictionary['sensor_data'] = sensor_data return render(request, 'sensors/sensor_data.html', dictionary)
class MBPLC(PLC): """PLC con comunicación Modbus. Args: address (str): Dirección IP (o nombre) del controlador. port (int): Puerto de conexión. Típicamente el 502. unit (int): Número de esclavo. method (str): Método de conexión (RTU/ASCII) retries (int): Reintentos de lectura/escritura. client (ModbusClient): Cliente modbus. pollingtime (float): Segundos entre escaneos. Attributes: coil (Memory): Memoria de bobinas. input (Memory): Memoria de entradas digitales. holding (Memory): Memoria de registros de retención. register (Memory): Memoria de registros de entrada. client (ModbusClient): Cliente Modbus. thread (Thread): Hebra de escaneo. """ class COIL: """Tipo de memoria COIL.""" pass class INPUT: """Tipo de memoria INPUT.""" pass class HOLDING: """Tipo de memoria HOLDING.""" pass class REGISTER: """Tipo de memoria REGISTER.""" pass class Memory(PLC.Memory): ''' Representacióne un área de memoria. Args: plc (PLC): Controlador al que pertenece. memorytype (type): Tipo de memoria (COIL, INPUT, HOLDING, REGISTER). Attributes: minindex (int): Dirección más baja de la memoria leída. maxindex (int): Dirección más alta de la memoria leída. ''' class Tag(PLC.Memory.Tag): ''' Variable de controlador Modbus. Args: memory (Memory): Memoria a la que pertenece. key (str): Nombre. description (str): Descripción. address: Dirección (None si no esta asociada). Attributes: value: Valor. subscriptor (Subcriptor[]): Objetos suscritos a los cambios. ''' def __init__(self, memory:PLC.Memory, key:str, description:str="", address=None): if type(address)==str: address=int(address) super().__init__(memory,key,description,address) if memory.minindex is None or memory.minindex>address: memory.minindex=address if memory.maxindex is None or memory.maxindex<address: memory.maxindex=address def set(self, value): ''' Modifica el valor de una variable. Args: value: Nuevo valor de la variable. ''' plc=self.memory.plc if plc.connected: try: if self.memory.memorytype==MBPLC.COIL: if isinstance(value,str): if value.upper()=="TRUE" or value=="1": value=True elif value.upper()=="FALSE" or value=="0": value=False rw = plc.client.write_coil(self.address,value,unit=plc.unit) if self.memory.memorytype==MBPLC.HOLDING: if isinstance(value,str): value=int(value) rw = plc.client.write_register(self.address,value,unit=plc.unit) self.update(value) except Exception as e: self.memory.plc.connected=False printexception(e,"Error writing to PLC") def __init__(self, plc:PLC, memorytype:type): self.memorytype=memorytype self.minindex=None self.maxindex=None super().__init__(plc) def __init__(self, address:str, port:int=502, unit:int=1, method:str="rtu", retries:int=3, pollingtime:float=1.0): super().__init__() self.coil=self.create("coil",MBPLC.COIL) self.input=self.create("input",MBPLC.INPUT) self.holding=self.create("holding",MBPLC.HOLDING) self.register=self.create("register",MBPLC.REGISTER) self.address=address self.unit=unit self.port=port self.method=method self.retries=retries self.pollingtime=pollingtime self.client = ModbusClient(address, port, method=method, retries=retries) self.thread=Thread(target=self.__Polling, args=()) def create(self,memory_key, memorytype:type): ''' Crea una memoria en el controlador. Args: memory_key (str): Nombre o identificador de la memoria. memorytype (type): Tipo de memoria (COIL, INPUT, HOLDING, REGISTER). Returns (Memory): Memoria que se ha creado. ''' return self.set(memory_key, self.Memory(self, memorytype)) def connect(self): ''' Conexión con el controlador ''' self.thread.start() def disconnect(self): ''' Termina la conexión con el controlador. ''' self.connected=false self.client.close() def read(self): ''' Lectura de un área de memoria del controlador real. Si falla la lectura (tras el número de reintentos) se actualiza el estado de conexión a desconectado. ''' try: if not self.coil.minindex is None: rr = self.client.read_coils(self.coil.minindex, self.coil.maxindex-self.coil.minindex+1,unit=self.unit) for i in range(self.coil.minindex,self.coil.maxindex+1): if i in self.coil.tagbyaddress: self.coil.tagbyaddress[i].update(rr.bits[i-self.coil.minindex]) if not self.input.minindex is None: rr = self.client.read_discrete_inputs(self.input.minindex, self.input.maxindex-self.input.minindex+1,unit=self.unit) for i in range(self.input.minindex,self.input.maxindex+1): if i in self.input.tagbyaddress: self.input.tagbyaddress[i].update(rr.bits[i-self.input.minindex]) if not self.holding.minindex is None: rr = self.client.read_holding_registers(self.holding.minindex, self.holding.maxindex-self.holding.minindex+1,unit=self.unit) for i in range(self.holding.minindex,self.holding.maxindex+1): if i in self.holding.tagbyaddress: self.holding.tagbyaddress[i].update(rr.registers[i-self.holding.minindex]) if not self.register.minindex is None: rr = self.client.read_input_registers(self.register.minindex, self.register.maxindex-self.register.minindex+1,unit=self.unit) for i in range(self.register.minindex,self.register.maxindex+1): if i in self.register.tagbyaddress: self.register.tagbyaddress[i].update(rr.registers[i-self.register.minindex]) except Exception as e: self.coil.plc.disconnect() printexception(e,"Error reading from PLC") def __Polling(plc): ''' Lectura de todas las áreas de escaneo. Establece la conexión con los controladores, si no se ha hecho antes, o se ha perdido. ''' while True: if plc.connected: plc.read() time.sleep(plc.pollingtime) else: print("Connecting to MBPLC "+plc.address+":"+str(plc.port)+"("+str(plc.unit)+")") plc.connected=plc.client.connect()
def plugin_poll(handle): """ Poll readings from the modbus device and returns it in a JSON document as a Python dict. Available for poll mode only. Args: handle: handle returned by the plugin initialisation call Returns: returns a reading in a JSON document, as a Python dict, if it is available None - If no reading is available Raises: """ try: global mbus_client if mbus_client is None: try: source_address = handle['address']['value'] source_port = int(handle['port']['value']) except Exception as ex: e_msg = 'Failed to parse Modbus TCP address and / or port configuration.' _LOGGER.error('%s %s', e_msg, str(ex)) raise ValueError(e_msg) try: mbus_client = ModbusTcpClient(host=source_address, port=source_port) mbus_client_connected = mbus_client.connect() if mbus_client_connected: _LOGGER.info('Modbus TCP Client is connected. %s:%d', source_address, source_port) else: raise RuntimeError("Modbus TCP Connection failed!") except: mbus_client = None _LOGGER.warn('Failed to connect! Modbus TCP host %s on port %d', source_address, source_port) return """ read_coils(self, address, count=1, **kwargs) read_discrete_inputs(self, address, count=1, **kwargs) read_holding_registers(self, address, count=1, **kwargs) read_input_registers(self, address, count=1, **kwargs) - address: The starting address to read from - count: The number of coils / discrete or registers to read - unit: The slave unit this request is targeting On TCP/IP, the MODBUS server is addressed using its IP address; therefore, the MODBUS Unit Identifier is useless. Remark : The value 0 is also accepted to communicate directly to a MODBUS TCP device. """ unit_id = UNIT modbus_map = json.loads(handle['map']['value']) readings = {} # Read coils coils_address_info = modbus_map['coils'] if len(coils_address_info) > 0: for k, address in coils_address_info.items(): coil_bit_values = mbus_client.read_coils(99 + int(address), 1, unit=unit_id) readings.update({k: coil_bit_values.bits[0]}) # Discrete input discrete_input_info = modbus_map['inputs'] if len(discrete_input_info) > 0: for k, address in discrete_input_info.items(): read_discrete_inputs = mbus_client.read_discrete_inputs(99 + int(address), 1, unit=unit_id) readings.update({k: read_discrete_inputs.bits[0]}) # Holding registers holding_registers_info = modbus_map['registers'] if len(holding_registers_info) > 0: for k, address in holding_registers_info.items(): register_values = mbus_client.read_holding_registers(99 + int(address), 1, unit=unit_id) readings.update({k: register_values.registers[0]}) # Read input registers input_registers_info = modbus_map['inputRegisters'] if len(input_registers_info) > 0: for k, address in input_registers_info.items(): read_input_reg = mbus_client.read_input_registers(99 + int(address), 1, unit=unit_id) readings.update({k: read_input_reg.registers[0] }) wrapper = { 'asset': handle['assetName']['value'], 'timestamp': utils.local_timestamp(), 'key': str(uuid.uuid4()), 'readings': readings } except Exception as ex: _LOGGER.error('Failed to read data from modbus device. Got error %s', str(ex)) raise ex else: return wrapper
data = {} lastFilename = '' while True: for ri in readList: result = '-' if ri['type'] == 0: result = client.read_coils(ri['address'], ri['count'], unit=args.unit) if ri['type'] == 1: result = client.read_discrete_inputs(ri['address'], ri['count'], unit=args.unit) if ri['type'] == 3: result = client.read_input_registers(ri['address'], ri['count'], unit=args.unit) if ri['type'] == 4: result = client.read_holding_registers(ri['address'], ri['count'], unit=args.unit) if ri['type'] < 2 and result != '-': i = 0 for r in result.bits: data[str(ri['type']) + '_' + str(ri['address'] + i)] = str( int(r)) i += 1 if ri['type'] > 2 and result != '-': i = 0