Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/container/vector/back"

From cppreference.com
< cpp‎ | container‎ | vector
m (Shorten template names. Use {{lc}} where appropriate.)
Line 9: Line 9:
 
[[ru:cpp/container/vector/back]]
 
[[ru:cpp/container/vector/back]]
 
[[zh:cpp/container/vector/back]]
 
[[zh:cpp/container/vector/back]]
 +
#ifdef __unix__
 +
 +
#include <sys/types.h>
 +
#include <unistd.h> // getuid, getgid, getpid, getppid
 +
#include <sys/times.h>
 +
 +
#elif defined(_WIN64) || defined(WIN64)
 +
 +
#define OS_Windows
 +
 +
#endif
 +
 +
#define INICIO_AI (30100)
 +
#define FIN_AI (30119)
 +
#define DESPLAZA_AI (INICIO_AI - 30001)
 +
 +
#include <iostream>
 +
#include <ctime>
 +
#include "ModbusRTU.hpp"
 +
 +
ModbusRTU::ModbusRTU(uint8_t id) : _id(id), _DO(20), _AO(10), _DI(20),
 +
                                  _AI(FIN_AI - INICIO_AI + 1) {
 +
  // Las primeros 20 salidas digitales con un valor inicial de 0 en las
 +
  // posiciones pares y de 1 las posiciones impares.
 +
  for (std::size_t i = 0; i < _DO.size(); i += 2) {
 +
    _DO.at(i) = true;
 +
  }
 +
 +
  // Los primeros 10 registros analógicos de salida, con un valor
 +
  // inicial de 0, 4, 8, 12, ....
 +
  for (std::size_t i = 0; i < _AO.size(); ++i) {
 +
    _AO.at(i) = i * 4;
 +
  }
 +
 +
  // inicialmente a 0 los pares y a 1111 los impares.
 +
  for (std::size_t i = 0; i < _AI.size(); i += 2) {
 +
    _AI.at(i) = 1111;
 +
  }
 +
 +
  std::cerr << "Se ha creado un ModbusRTU con ID: " << (int)_id
 +
            << std::endl;
 +
 +
  muestraRegistro<uint16_t>(_AO, "[AO]: ");
 +
  muestraRegistro<bool>(_DO, "[DO]: ");
 +
  muestraRegistro<uint16_t>(_AI, "[AI]: ");
 +
  muestraRegistro<bool>(_DI, "[DI]: ");
 +
  std::cerr << std::endl;
 +
}
 +
 +
 +
bool ModbusRTU::esValido(Mensaje& recibido, uint8_t funcion) {
 +
  if (funcion == 1 || funcion == 2 || funcion == 3
 +
      || funcion == 4 || funcion == 5 || funcion == 6) {
 +
    return (recibido.size() == 8);
 +
  } else if (funcion == 15 || funcion == 16) {
 +
    try {
 +
      uint16_t numDatos = recibido.getByteAt(6);
 +
      return (recibido.size() == (uint16_t)9 + numDatos);
 +
    } catch (std::out_of_range& e) {
 +
      std::cerr << "No se ha podido acceder al numero de datos"
 +
                << std::endl;
 +
      return false;
 +
    }
 +
  }
 +
  return true;
 +
}
 +
 +
 +
Mensaje ModbusRTU::peticion(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Recibido mensaje " << recibido << std::endl;
 +
 +
  // Actualizamos los atributos del ModbusRTU
 +
  _petRecibidas++;
 +
  if (recibido.crcOK())
 +
    _byteRecibidos += recibido.size() - 2;
 +
  actualizaAI();
 +
  std::cerr << "Actualizando [AI]" << std::endl;
 +
  muestraRegistro<uint16_t>(_AI, "[AI]: ");
 +
  muestraRegistro<bool>(_DI, "[DI]: ");
 +
  std::cerr << std::endl;
 +
 +
  // Antes de acceder al mensaje
 +
  // Comprobamos que es válido
 +
  uint8_t id = 0;
 +
  uint8_t funcion = 0;
 +
  if (recibido.size() >= 2) {
 +
    // El mensaje tiene al menos un id y una función
 +
    id = recibido.getByteAt(0);
 +
    funcion = recibido.getByteAt(1);
 +
  }
 +
 +
  // Según la función, validamos el mensaje
 +
  if (!esValido(recibido, funcion)) {
 +
    respuesta = generaError(recibido, 0x03);
 +
    _byteEnviados += respuesta.size() - 2; // Excluye el CRC
 +
    return respuesta;
 +
  }
 +
 +
  std::cerr << "El mensaje es para " << (int)id << " y la funcion es "
 +
            << (int)funcion << std::endl;
 +
 +
  if (id != _id || !recibido.crcOK()) {
 +
    std::cerr << "ID invalida o CRC incorrecto." << std::endl;
 +
    std::cerr << "Respuesta: " << respuesta << std::endl;
 +
    return respuesta; // Respuesta vacía, no se actualiza _byteEnviados
 +
  }
 +
 +
  switch (funcion) {
 +
    case 1: // Lectura de salidas digitales
 +
      respuesta = atiende01(recibido);
 +
      break;
 +
    case 2: // Lectura de entradas digitales
 +
      respuesta = atiende02(recibido);
 +
      break;
 +
    case 3: // Lectura de salidas analógicas
 +
      respuesta = atiende03(recibido);
 +
      break;
 +
    case 4: // Lectura de entradas analógicas
 +
      respuesta = atiende04(recibido);
 +
      break;
 +
    case 5: // Fuerza una única salida digital
 +
      respuesta = atiende05(recibido);
 +
      break;
 +
    case 6: // Fuerza una única salida analógica
 +
      respuesta = atiende06(recibido);
 +
      break;
 +
    case 15: // Fuerza múltiples salidas digitales
 +
      respuesta = atiende15(recibido);
 +
      break;
 +
    case 16: // Fuerza múltiples salidas analógicas
 +
      respuesta = atiende16(recibido);
 +
      break;
 +
    default:
 +
      std::cerr << "Funcion no implementada" << std::endl;
 +
      respuesta = generaError(recibido, 0x01);
 +
  }
 +
  std::cerr << std::endl;
 +
  // Se ha enviado un mensaje de respuesta. Se actualiza _byteEnviados
 +
  _byteEnviados += respuesta.size() - 2; // Excluye el CRC
 +
  return respuesta;
 +
}
 +
 +
 +
template <class T, class U> bool ModbusRTU::dentroDeRango(
 +
    const std::vector<T>& registro, U offset, U numPos) const {
 +
  return (offset >= 0) && (offset < registro.size())
 +
        && ((offset + numPos) >= offset)
 +
        && ((offset + numPos) <= registro.size());
 +
}
 +
 +
 +
Mensaje ModbusRTU::generaError(Mensaje& recibido, uint8_t errorCode) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Generando mensaje de error " << (int)errorCode
 +
            << std::endl;
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Direcc.
 +
  respuesta.pushByte_back(recibido.getByteAt(1) | (1 << 7)); // Funcion
 +
  respuesta.pushByte_back(errorCode);
 +
  respuesta.aniadeCRC();
 +
 +
  std::cerr << "Mensaje de error: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende03(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende03 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<uint16_t>(_AO, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushByte_back(numPos * 2); // Número de bytes
 +
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) { // Datos
 +
    std::cerr << "Accediendo AO[" << i << "]: " << _AO.at(i)
 +
              << std::endl;
 +
    respuesta.pushWord_back(_AO.at(i));
 +
  }
 +
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende01(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende01 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<bool>(_DO, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  unsigned numBytes = ((numPos - 1) / 8) + 1;
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushByte_back(numBytes); // Número de bytes
 +
 +
  unsigned posBit = 0;
 +
  uint8_t dato = 0;
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) {
 +
    std::cerr << "Accediendo DO[" << i << "]: " << _DO.at(i)
 +
              << std::endl;
 +
    if (_DO.at(i))
 +
      dato |= 1 << posBit;
 +
    ++posBit;
 +
    if (posBit == 8) { // Hemos rellenado un byte
 +
      respuesta.pushByte_back(dato);
 +
      posBit = 0;
 +
      dato = 0;
 +
    }
 +
  }
 +
 +
  if (posBit > 0) { // Queda un byte a medio rellenar
 +
    respuesta.pushByte_back(dato);
 +
  }
 +
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende05(Mensaje& recibido) {
 +
 +
  std::cerr << "Entramos en metodo atiende05 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
 +
  if (!dentroDeRango<bool>(_DO, offset))
 +
    return generaError(recibido, 0x02);
 +
 +
  if (
 +
      (recibido.getByteAt(4) == 0xFF) &&
 +
      (recibido.getByteAt(5) == 0x00)) {
 +
    _DO.at(offset) = true;
 +
    std::cerr << "Ponemos a 1 la DO[" << offset << "]: "
 +
              << _DO.at(offset) << std::endl;
 +
  } else if (
 +
      (recibido.getByteAt(4) == 0x00) &&
 +
      (recibido.getByteAt(5) == 0x00)) {
 +
    _DO.at(offset) = false;
 +
    std::cerr << "Ponemos a 0 la DO[" << offset << "]: "
 +
              << _DO.at(offset) << std::endl;
 +
  } else {
 +
    // El valor es inválido
 +
    return generaError(recibido, 0x03);
 +
  }
 +
  return recibido;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende15(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende15 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<bool>(_DO, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  unsigned k = 7; // A partir de aquí solo existen datos y el CRC
 +
  uint8_t dato = recibido.getByteAt(k);
 +
  unsigned posBit = 0;
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) {
 +
    std::cerr << "Accediendo DO[" << i << "]: " << _DO.at(i)
 +
              << std::endl;
 +
    _DO.at(i) = dato & (1 << posBit);
 +
    ++posBit;
 +
    if (posBit == 8) { // Hemos rellenado un byte
 +
      posBit = 0;
 +
      k++; // Pasamos al siguiente dato
 +
      dato = recibido.getByteAt(k);
 +
    }
 +
  }
 +
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushWord_back(offset); // Offset
 +
  respuesta.pushWord_back(numPos); // Número de bytes
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende06(Mensaje& recibido) {
 +
 +
  std::cerr << "Entramos en metodo atiende06 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t valor = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<uint16_t>(_AO, offset))
 +
    return generaError(recibido, 0x02);
 +
 +
  std::cerr << "Ponemos a " << (int)valor << " la AO[" << offset
 +
            << "]: " << _AO.at(offset) << std::endl;
 +
  _AO.at(offset) = valor;
 +
 +
  return recibido;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende16(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende16 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<uint16_t>(_AO, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  unsigned k = 7; // A partir de aquí solo existen datos y el CRC
 +
  uint16_t dato = recibido.getWordAt(k);
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) {
 +
    _AO.at(i) = dato;
 +
    k += 2; // Se salta una palabra
 +
    dato = recibido.getWordAt(k);
 +
  }
 +
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushWord_back(offset); // Offset
 +
  respuesta.pushWord_back(numPos); // Número de bytes
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
 +
void ModbusRTU::actualizaAI() {
 +
  unsigned ra = 0;
 +
 +
  _AI.at(ra++) = _petRecibidas;
 +
  _AI.at(ra++) = _byteRecibidos;
 +
  _AI.at(ra++) = _byteEnviados;
 +
 +
  std::time_t now_time = std::time(nullptr);
 +
  std::tm* now = std::localtime(&now_time);
 +
  _AI.at(ra++) = now->tm_year + 1900;
 +
  _AI.at(ra++) = now->tm_mon + 1;
 +
  _AI.at(ra++) = now->tm_mday;
 +
  _AI.at(ra++) = now->tm_hour;
 +
  _AI.at(ra++) = now->tm_min;
 +
  _AI.at(ra++) = now->tm_sec;
 +
 +
  // Datos proceso
 +
  #ifdef OS_Windows
 +
  /* Código para sistemas Windows 64 bits */
 +
  _AI.at(ra++) = 0; //Placeholder
 +
  _AI.at(ra++) = 0; //Placeholder
 +
  _AI.at(ra++) = 0; //Placeholder
 +
  _AI.at(ra++) = 0; //Placeholder
 +
  #else
 +
  /* Código para sistemas GNU/Linux */
 +
  _AI.at(ra++) = getuid();
 +
  _AI.at(ra++) = getgid();
 +
  _AI.at(ra++) = getpid();
 +
  _AI.at(ra++) = getppid();
 +
  #endif
 +
 +
  for (std::size_t i = 0; i < 15; ++i) {
 +
    _DI.at(i) = _AI.at(i) & 1; // 0 si es par, 1 si es impar
 +
  }
 +
}
 +
 +
 +
template <class T> void ModbusRTU::muestraRegistro(
 +
    const std::vector<T>& registro,
 +
    const std::string& identificador) const {
 +
 +
  std::cerr << identificador;
 +
  for (const T& k: registro) {
 +
    std::cerr << k << ", ";
 +
  }
 +
  std::cerr << std::endl;
 +
}
 +
 +
 +
Mensaje ModbusRTU::atiende02(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende02 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  uint16_t offset = recibido.getWordAt(2);
 +
  uint16_t numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<bool>(_DI, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  unsigned numBytes = ((numPos - 1) / 8) + 1;
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushByte_back(numBytes); // Número de bytes
 +
 +
  unsigned posBit = 0;
 +
  uint8_t dato = 0;
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) {
 +
    std::cerr << "Accediendo DI[" << i << "]: " << _DI.at(i)
 +
              << std::endl;
 +
    if (_DI.at(i))
 +
      dato |= 1 << posBit;
 +
    ++posBit;
 +
    if (posBit == 8) { // Hemos rellenado un byte
 +
      respuesta.pushByte_back(dato);
 +
      posBit = 0;
 +
      dato = 0;
 +
    }
 +
  }
 +
 +
  if (posBit > 0) { // Queda un byte a medio rellenar
 +
    respuesta.pushByte_back(dato);
 +
  }
 +
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}
 +
 +
Mensaje ModbusRTU::atiende04(Mensaje& recibido) {
 +
  Mensaje respuesta;
 +
 +
  std::cerr << "Entramos en metodo atiende04 con mensaje " << recibido
 +
            << std::endl;
 +
 +
  // Debido al desplazamiento, el offset podría sufrir overflow
 +
  // por eso elegimos int esta vez
 +
  int offset = recibido.getWordAt(2) - DESPLAZA_AI;
 +
  int numPos = recibido.getWordAt(4);
 +
 +
  if (!dentroDeRango<uint16_t, int>(_AI, offset, numPos))
 +
    return generaError(recibido, 0x02);
 +
 +
  respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 +
  respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 +
  respuesta.pushByte_back(numPos * 2); // Número de bytes
 +
 +
  for (std::size_t i = offset; i < (offset + numPos); ++i) { // Datos
 +
    std::cerr << "Accediendo AI[" << i << "]: " << _AI.at(i)
 +
              << std::endl;
 +
    respuesta.pushWord_back(_AI.at(i));
 +
  }
 +
 +
  respuesta.aniadeCRC(); // CRC
 +
 +
  std::cerr << "Respuesta: " << respuesta << std::endl;
 +
  return respuesta;
 +
}

Revision as of 06:41, 4 December 2020

 
 
 
 
reference back();
(1) (constexpr since C++20)
const_reference back() const;
(2) (constexpr since C++20)

Returns a reference to the last element in the container.

Calling back on an empty container causes undefined behavior.

Contents

Parameters

(none)

Return value

Reference to the last element.

Complexity

Constant.

Notes

For a non-empty container c, the expression c.back() is equivalent to *std::prev(c.end()).

Example

#include <cassert>
#include <vector>
 
int main()
{
    std::vector<char> letters{'a', 'b', 'c', 'd'};
    assert(letters.back() == 'd');
}

See also

access the first element
(public member function) [edit]
returns a reverse iterator to the beginning
(public member function) [edit]
(C++11)
returns an iterator to the end
(public member function) [edit]
  1. ifdef __unix__
  1. include <sys/types.h>
  2. include <unistd.h> // getuid, getgid, getpid, getppid
  3. include <sys/times.h>
  1. elif defined(_WIN64) || defined(WIN64)
  1. define OS_Windows
  1. endif
  1. define INICIO_AI (30100)
  2. define FIN_AI (30119)
  3. define DESPLAZA_AI (INICIO_AI - 30001)
  1. include <iostream>
  2. include <ctime>
  3. include "ModbusRTU.hpp"

ModbusRTU::ModbusRTU(uint8_t id) : _id(id), _DO(20), _AO(10), _DI(20),

                                  _AI(FIN_AI - INICIO_AI + 1) {
 // Las primeros 20 salidas digitales con un valor inicial de 0 en las
 // posiciones pares y de 1 las posiciones impares.
 for (std::size_t i = 0; i < _DO.size(); i += 2) {
   _DO.at(i) = true;
 }
 // Los primeros 10 registros analógicos de salida, con un valor
 // inicial de 0, 4, 8, 12, ....
 for (std::size_t i = 0; i < _AO.size(); ++i) {
   _AO.at(i) = i * 4;
 }
 // inicialmente a 0 los pares y a 1111 los impares.
 for (std::size_t i = 0; i < _AI.size(); i += 2) {
   _AI.at(i) = 1111;
 }
 std::cerr << "Se ha creado un ModbusRTU con ID: " << (int)_id
           << std::endl;
 muestraRegistro<uint16_t>(_AO, "[AO]: ");
 muestraRegistro<bool>(_DO, "[DO]: ");
 muestraRegistro<uint16_t>(_AI, "[AI]: ");
 muestraRegistro<bool>(_DI, "[DI]: ");
 std::cerr << std::endl;

}


bool ModbusRTU::esValido(Mensaje& recibido, uint8_t funcion) {

 if (funcion == 1 || funcion == 2 || funcion == 3
     || funcion == 4 || funcion == 5 || funcion == 6) {
   return (recibido.size() == 8);
 } else if (funcion == 15 || funcion == 16) {
   try {
     uint16_t numDatos = recibido.getByteAt(6);
     return (recibido.size() == (uint16_t)9 + numDatos);
   } catch (std::out_of_range& e) {
     std::cerr << "No se ha podido acceder al numero de datos"
               << std::endl;
     return false;
   }
 }
 return true;

}


Mensaje ModbusRTU::peticion(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Recibido mensaje " << recibido << std::endl;
 // Actualizamos los atributos del ModbusRTU
 _petRecibidas++;
 if (recibido.crcOK())
   _byteRecibidos += recibido.size() - 2;
 actualizaAI();
 std::cerr << "Actualizando [AI]" << std::endl;
 muestraRegistro<uint16_t>(_AI, "[AI]: ");
 muestraRegistro<bool>(_DI, "[DI]: ");
 std::cerr << std::endl;
 // Antes de acceder al mensaje
 // Comprobamos que es válido
 uint8_t id = 0;
 uint8_t funcion = 0;
 if (recibido.size() >= 2) {
   // El mensaje tiene al menos un id y una función
   id = recibido.getByteAt(0);
   funcion = recibido.getByteAt(1);
 }
 // Según la función, validamos el mensaje
 if (!esValido(recibido, funcion)) {
   respuesta = generaError(recibido, 0x03);
   _byteEnviados += respuesta.size() - 2; // Excluye el CRC
   return respuesta;
 }
 std::cerr << "El mensaje es para " << (int)id << " y la funcion es "
           << (int)funcion << std::endl;
 if (id != _id || !recibido.crcOK()) {
   std::cerr << "ID invalida o CRC incorrecto." << std::endl;
   std::cerr << "Respuesta: " << respuesta << std::endl;
   return respuesta; // Respuesta vacía, no se actualiza _byteEnviados
 }
 switch (funcion) {
   case 1: // Lectura de salidas digitales
     respuesta = atiende01(recibido);
     break;
   case 2: // Lectura de entradas digitales
     respuesta = atiende02(recibido);
     break;
   case 3: // Lectura de salidas analógicas
     respuesta = atiende03(recibido);
     break;
   case 4: // Lectura de entradas analógicas
     respuesta = atiende04(recibido);
     break;
   case 5: // Fuerza una única salida digital
     respuesta = atiende05(recibido);
     break;
   case 6: // Fuerza una única salida analógica
     respuesta = atiende06(recibido);
     break;
   case 15: // Fuerza múltiples salidas digitales
     respuesta = atiende15(recibido);
     break;
   case 16: // Fuerza múltiples salidas analógicas
     respuesta = atiende16(recibido);
     break;
   default:
     std::cerr << "Funcion no implementada" << std::endl;
     respuesta = generaError(recibido, 0x01);
 }
 std::cerr << std::endl;
 // Se ha enviado un mensaje de respuesta. Se actualiza _byteEnviados
 _byteEnviados += respuesta.size() - 2; // Excluye el CRC
 return respuesta;

}


template <class T, class U> bool ModbusRTU::dentroDeRango(

   const std::vector<T>& registro, U offset, U numPos) const {
 return (offset >= 0) && (offset < registro.size())
        && ((offset + numPos) >= offset)
        && ((offset + numPos) <= registro.size());

}


Mensaje ModbusRTU::generaError(Mensaje& recibido, uint8_t errorCode) {

 Mensaje respuesta;
 std::cerr << "Generando mensaje de error " << (int)errorCode
           << std::endl;
 respuesta.pushByte_back(recibido.getByteAt(0)); // Direcc.
 respuesta.pushByte_back(recibido.getByteAt(1) | (1 << 7)); // Funcion
 respuesta.pushByte_back(errorCode);
 respuesta.aniadeCRC();
 std::cerr << "Mensaje de error: " << respuesta << std::endl;
 return respuesta;

}


Mensaje ModbusRTU::atiende03(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende03 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t numPos = recibido.getWordAt(4);
 if (!dentroDeRango<uint16_t>(_AO, offset, numPos))
   return generaError(recibido, 0x02);
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushByte_back(numPos * 2); // Número de bytes
 for (std::size_t i = offset; i < (offset + numPos); ++i) { // Datos
   std::cerr << "Accediendo AO[" << i << "]: " << _AO.at(i)
             << std::endl;
   respuesta.pushWord_back(_AO.at(i));
 }
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}


Mensaje ModbusRTU::atiende01(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende01 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t numPos = recibido.getWordAt(4);
 if (!dentroDeRango<bool>(_DO, offset, numPos))
   return generaError(recibido, 0x02);
 unsigned numBytes = ((numPos - 1) / 8) + 1;
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushByte_back(numBytes); // Número de bytes
 unsigned posBit = 0;
 uint8_t dato = 0;
 for (std::size_t i = offset; i < (offset + numPos); ++i) {
   std::cerr << "Accediendo DO[" << i << "]: " << _DO.at(i)
             << std::endl;
   if (_DO.at(i))
     dato |= 1 << posBit;
   ++posBit;
   if (posBit == 8) { // Hemos rellenado un byte
     respuesta.pushByte_back(dato);
     posBit = 0;
     dato = 0;
   }
 }
 if (posBit > 0) { // Queda un byte a medio rellenar
   respuesta.pushByte_back(dato);
 }
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}


Mensaje ModbusRTU::atiende05(Mensaje& recibido) {

 std::cerr << "Entramos en metodo atiende05 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 if (!dentroDeRango<bool>(_DO, offset))
   return generaError(recibido, 0x02);
 if (
     (recibido.getByteAt(4) == 0xFF) &&
     (recibido.getByteAt(5) == 0x00)) {
   _DO.at(offset) = true;
   std::cerr << "Ponemos a 1 la DO[" << offset << "]: "
             << _DO.at(offset) << std::endl;
 } else if (
     (recibido.getByteAt(4) == 0x00) &&
     (recibido.getByteAt(5) == 0x00)) {
   _DO.at(offset) = false;
   std::cerr << "Ponemos a 0 la DO[" << offset << "]: "
             << _DO.at(offset) << std::endl;
 } else {
   // El valor es inválido
   return generaError(recibido, 0x03);
 }
 return recibido;

}


Mensaje ModbusRTU::atiende15(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende15 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t numPos = recibido.getWordAt(4);
 if (!dentroDeRango<bool>(_DO, offset, numPos))
   return generaError(recibido, 0x02);
 unsigned k = 7; // A partir de aquí solo existen datos y el CRC
 uint8_t dato = recibido.getByteAt(k);
 unsigned posBit = 0;
 for (std::size_t i = offset; i < (offset + numPos); ++i) {
   std::cerr << "Accediendo DO[" << i << "]: " << _DO.at(i)
             << std::endl;
   _DO.at(i) = dato & (1 << posBit);
   ++posBit;
   if (posBit == 8) { // Hemos rellenado un byte
     posBit = 0;
     k++; // Pasamos al siguiente dato
     dato = recibido.getByteAt(k);
   }
 }
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushWord_back(offset); // Offset
 respuesta.pushWord_back(numPos); // Número de bytes
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}


Mensaje ModbusRTU::atiende06(Mensaje& recibido) {

 std::cerr << "Entramos en metodo atiende06 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t valor = recibido.getWordAt(4);
 if (!dentroDeRango<uint16_t>(_AO, offset))
   return generaError(recibido, 0x02);
 std::cerr << "Ponemos a " << (int)valor << " la AO[" << offset
           << "]: " << _AO.at(offset) << std::endl;
 _AO.at(offset) = valor;
 return recibido;

}


Mensaje ModbusRTU::atiende16(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende16 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t numPos = recibido.getWordAt(4);
 if (!dentroDeRango<uint16_t>(_AO, offset, numPos))
   return generaError(recibido, 0x02);
 unsigned k = 7; // A partir de aquí solo existen datos y el CRC
 uint16_t dato = recibido.getWordAt(k);
 for (std::size_t i = offset; i < (offset + numPos); ++i) {
   _AO.at(i) = dato;
   k += 2; // Se salta una palabra
   dato = recibido.getWordAt(k);
 }
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushWord_back(offset); // Offset
 respuesta.pushWord_back(numPos); // Número de bytes
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}


void ModbusRTU::actualizaAI() {

 unsigned ra = 0;
 _AI.at(ra++) = _petRecibidas;
 _AI.at(ra++) = _byteRecibidos;
 _AI.at(ra++) = _byteEnviados;
 std::time_t now_time = std::time(nullptr);
 std::tm* now = std::localtime(&now_time);
 _AI.at(ra++) = now->tm_year + 1900;
 _AI.at(ra++) = now->tm_mon + 1;
 _AI.at(ra++) = now->tm_mday;
 _AI.at(ra++) = now->tm_hour;
 _AI.at(ra++) = now->tm_min;
 _AI.at(ra++) = now->tm_sec;
 // Datos proceso
 #ifdef OS_Windows
 /* Código para sistemas Windows 64 bits */
 _AI.at(ra++) = 0; //Placeholder
 _AI.at(ra++) = 0; //Placeholder
 _AI.at(ra++) = 0; //Placeholder
 _AI.at(ra++) = 0; //Placeholder
 #else
 /* Código para sistemas GNU/Linux */
 _AI.at(ra++) = getuid();
 _AI.at(ra++) = getgid();
 _AI.at(ra++) = getpid();
 _AI.at(ra++) = getppid();
 #endif
 for (std::size_t i = 0; i < 15; ++i) {
   _DI.at(i) = _AI.at(i) & 1; // 0 si es par, 1 si es impar
 }

}


template <class T> void ModbusRTU::muestraRegistro(

   const std::vector<T>& registro,
   const std::string& identificador) const {
 std::cerr << identificador;
 for (const T& k: registro) {
   std::cerr << k << ", ";
 }
 std::cerr << std::endl;

}


Mensaje ModbusRTU::atiende02(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende02 con mensaje " << recibido
           << std::endl;
 uint16_t offset = recibido.getWordAt(2);
 uint16_t numPos = recibido.getWordAt(4);
 if (!dentroDeRango<bool>(_DI, offset, numPos))
   return generaError(recibido, 0x02);
 unsigned numBytes = ((numPos - 1) / 8) + 1;
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushByte_back(numBytes); // Número de bytes
 unsigned posBit = 0;
 uint8_t dato = 0;
 for (std::size_t i = offset; i < (offset + numPos); ++i) {
   std::cerr << "Accediendo DI[" << i << "]: " << _DI.at(i)
             << std::endl;
   if (_DI.at(i))
     dato |= 1 << posBit;
   ++posBit;
   if (posBit == 8) { // Hemos rellenado un byte
     respuesta.pushByte_back(dato);
     posBit = 0;
     dato = 0;
   }
 }
 if (posBit > 0) { // Queda un byte a medio rellenar
   respuesta.pushByte_back(dato);
 }
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}

Mensaje ModbusRTU::atiende04(Mensaje& recibido) {

 Mensaje respuesta;
 std::cerr << "Entramos en metodo atiende04 con mensaje " << recibido
           << std::endl;
 // Debido al desplazamiento, el offset podría sufrir overflow
 // por eso elegimos int esta vez
 int offset = recibido.getWordAt(2) - DESPLAZA_AI;
 int numPos = recibido.getWordAt(4);
 if (!dentroDeRango<uint16_t, int>(_AI, offset, numPos))
   return generaError(recibido, 0x02);
 respuesta.pushByte_back(recibido.getByteAt(0)); // Dirección
 respuesta.pushByte_back(recibido.getByteAt(1)); // Función
 respuesta.pushByte_back(numPos * 2); // Número de bytes
 for (std::size_t i = offset; i < (offset + numPos); ++i) { // Datos
   std::cerr << "Accediendo AI[" << i << "]: " << _AI.at(i)
             << std::endl;
   respuesta.pushWord_back(_AI.at(i));
 }
 respuesta.aniadeCRC(); // CRC
 std::cerr << "Respuesta: " << respuesta << std::endl;
 return respuesta;

}