UniSet  2.6.0
extensions/ModbusSlave/MBSlave.h
1 /*
2  * Copyright (c) 2015 Pavel Vainerman.
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation, version 2.1.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  * Lesser General Lesser Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 // -----------------------------------------------------------------------------
17 #ifndef _MBSlave_H_
18 #define _MBSlave_H_
19 // -----------------------------------------------------------------------------
20 #include <ostream>
21 #include <string>
22 #include <memory>
23 #include <map>
24 #include <unordered_map>
25 #include <vector>
26 #include <condition_variable>
27 #include <atomic>
28 #include <mutex>
29 #include "UniSetObject.h"
30 #include "modbus/ModbusTypes.h"
31 #include "modbus/ModbusServerSlot.h"
32 #include "modbus/ModbusTCPServer.h"
33 #include "modbus/ModbusTCPServerSlot.h"
34 #include "PassiveTimer.h"
35 #include "Trigger.h"
36 #include "Mutex.h"
37 #include "SMInterface.h"
38 #include "SharedMemory.h"
39 #include "IOBase.h"
40 #include "VTypes.h"
41 #include "ThreadCreator.h"
42 #include "LogServer.h"
43 #include "LogAgregator.h"
44 #include "VMonitor.h"
45 // -----------------------------------------------------------------------------
46 #ifndef vmonit
47 #define vmonit( var ) vmon.add( #var, var )
48 #endif
49 // -------------------------------------------------------------------------
50 namespace uniset
51 {
52  // -----------------------------------------------------------------------------
307  // -----------------------------------------------------------------------------
309  class MBSlave:
310  public UniSetObject
311  {
312  public:
313  MBSlave( uniset::ObjectId objId, uniset::ObjectId shmID, const std::shared_ptr<SharedMemory>& ic = nullptr, const std::string& prefix = "mbs" );
314  virtual ~MBSlave();
315 
317  static std::shared_ptr<MBSlave> init_mbslave(int argc, const char* const* argv,
318  uniset::ObjectId shmID, const std::shared_ptr<SharedMemory>& ic = nullptr,
319  const std::string& prefix = "mbs" );
320 
322  static void help_print( int argc, const char* const* argv );
323 
324  static const int NoSafetyState = -1;
325 
326  enum AccessMode
327  {
328  amRW,
329  amRO,
330  amWO
331  };
332 
333  struct BitRegProperty;
334 
335  struct IOProperty:
336  public IOBase
337  {
338  ModbusRTU::ModbusData mbreg;
339  AccessMode amode;
340  VTypes::VType vtype;
341  size_t wnum;
342  size_t nbyte;
343  std::shared_ptr<BitRegProperty> bitreg;
344  ModbusRTU::RegID regID;
345 
346  IOProperty():
347  mbreg(0),
348  amode(amRW),
349  vtype(VTypes::vtUnknown),
350  wnum(0),
351  nbyte(0),
352  regID(0)
353  {}
354 
355  friend std::ostream& operator<<( std::ostream& os, IOProperty& p );
356  };
357 
358 
360  {
361  typedef std::vector<IOProperty> BitSensorMap;
362 
363  ModbusRTU::ModbusData mbreg;
364  BitSensorMap bvec;
365 
366  BitRegProperty(): mbreg(0), bvec(ModbusRTU::BitsPerData) {}
367 
369  bool check( const IOController_i::SensorInfo& si );
370 
371  friend std::ostream& operator<<( std::ostream& os, BitRegProperty& p );
372  friend std::ostream& operator<<( std::ostream& os, BitRegProperty* p );
373  };
374 
375  inline long getAskCount()
376  {
377  return askCount;
378  }
379 
380  inline std::shared_ptr<LogAgregator> getLogAggregator()
381  {
382  return loga;
383  }
384  inline std::shared_ptr<DebugStream> log()
385  {
386  return mblog;
387  }
388 
389  virtual uniset::SimpleInfo* getInfo( const char* userparam = 0 ) override;
390 
391  protected:
392 
394  ModbusRTU::mbErrCode readCoilStatus( ModbusRTU::ReadCoilMessage& query,
395  ModbusRTU::ReadCoilRetMessage& reply );
397  ModbusRTU::mbErrCode readInputStatus( ModbusRTU::ReadInputStatusMessage& query,
398  ModbusRTU::ReadInputStatusRetMessage& reply );
399 
401  ModbusRTU::mbErrCode readOutputRegisters( ModbusRTU::ReadOutputMessage& query,
402  ModbusRTU::ReadOutputRetMessage& reply );
403 
405  ModbusRTU::mbErrCode readInputRegisters( ModbusRTU::ReadInputMessage& query,
406  ModbusRTU::ReadInputRetMessage& reply );
407 
409  ModbusRTU::mbErrCode forceSingleCoil( ModbusRTU::ForceSingleCoilMessage& query,
410  ModbusRTU::ForceSingleCoilRetMessage& reply );
411 
413  ModbusRTU::mbErrCode forceMultipleCoils( ModbusRTU::ForceCoilsMessage& query,
414  ModbusRTU::ForceCoilsRetMessage& reply );
415 
416 
418  ModbusRTU::mbErrCode writeOutputRegisters( ModbusRTU::WriteOutputMessage& query,
419  ModbusRTU::WriteOutputRetMessage& reply );
420 
422  ModbusRTU::mbErrCode writeOutputSingleRegister( ModbusRTU::WriteSingleOutputMessage& query,
423  ModbusRTU::WriteSingleOutputRetMessage& reply );
424 
426  // ModbusRTU::mbErrCode journalCommand( ModbusRTU::JournalCommandMessage& query,
427  // ModbusRTU::JournalCommandRetMessage& reply );
428 
430  ModbusRTU::mbErrCode setDateTime( ModbusRTU::SetDateTimeMessage& query,
431  ModbusRTU::SetDateTimeRetMessage& reply );
432 
434  ModbusRTU::mbErrCode remoteService( ModbusRTU::RemoteServiceMessage& query,
435  ModbusRTU::RemoteServiceRetMessage& reply );
436 
437  ModbusRTU::mbErrCode fileTransfer( ModbusRTU::FileTransferMessage& query,
438  ModbusRTU::FileTransferRetMessage& reply );
439 
440  ModbusRTU::mbErrCode diagnostics( ModbusRTU::DiagnosticMessage& query,
441  ModbusRTU::DiagnosticRetMessage& reply );
442 
443  ModbusRTU::mbErrCode read4314( ModbusRTU::MEIMessageRDI& query,
444  ModbusRTU::MEIMessageRetRDI& reply );
445 
449  virtual ModbusRTU::mbErrCode checkRegister( ModbusRTU::ModbusData reg, ModbusRTU::ModbusData& val )
450  {
451  return ModbusRTU::erNoError;
452  }
453 
454  // т.к. в функциях (much_real_read,nuch_real_write) рассчёт на отсортированность IOMap
455  // то использовать unordered_map нельзя
456  typedef std::map<ModbusRTU::RegID, IOProperty> RegMap;
457 
458  typedef std::unordered_map<ModbusRTU::ModbusAddr, RegMap> IOMap;
459 
460  IOMap iomap;
462  // т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список
463  // и отдельно его проверяем потом
464  typedef std::list<IOBase> ThresholdList;
465  ThresholdList thrlist;
466 
467  std::shared_ptr<ModbusServerSlot> mbslot;
468  std::unordered_set<ModbusRTU::ModbusAddr> vaddr;
469  std::string default_mbaddr = { "" };
470 
471  xmlNode* cnode = { 0 };
472  std::string s_field = { "" };
473  std::string s_fvalue = { "" };
474  int default_mbfunc = {0}; // функция по умолчанию, для вычисления RegID
475 
476  std::shared_ptr<SMInterface> shm;
477 
478  virtual void sysCommand( const uniset::SystemMessage* msg ) override;
479  virtual void sensorInfo( const uniset::SensorMessage* sm ) override;
480  void askSensors( UniversalIO::UIOCommand cmd );
481  void waitSMReady();
482  virtual void execute_rtu();
483  virtual void execute_tcp();
484  virtual void updateStatistics();
485  virtual void updateTCPStatistics();
486  virtual void updateThresholds();
487  virtual void postReceiveEvent( ModbusRTU::mbErrCode res );
488 
489  virtual bool activateObject() override;
490  virtual bool deactivateObject() override;
491 
492  // действия при завершении работы
493  virtual void sigterm( int signo ) override;
494  virtual void finalThread();
495 
496  virtual void initIterators();
497  bool initItem( UniXML::iterator& it );
498  bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
499 
500  void readConfiguration();
501  bool check_item( UniXML::iterator& it );
502 
503  ModbusRTU::mbErrCode real_write( RegMap& rmap, const ModbusRTU::ModbusData regOKOK, ModbusRTU::ModbusData val, const int fn = 0 );
504  ModbusRTU::mbErrCode real_write( RegMap& rmap, const ModbusRTU::ModbusData regOKOK, ModbusRTU::ModbusData* dat, size_t& i, size_t count, const int fn = 0 );
505  ModbusRTU::mbErrCode real_read( RegMap& rmap, const ModbusRTU::ModbusData regOKOK, ModbusRTU::ModbusData& val, const int fn = 0 );
506  ModbusRTU::mbErrCode much_real_read( RegMap& rmap, const ModbusRTU::ModbusData regOKOK, ModbusRTU::ModbusData* dat, size_t count, const int fn = 0 );
507  ModbusRTU::mbErrCode much_real_write(RegMap& rmap, const ModbusRTU::ModbusData regOKOK, ModbusRTU::ModbusData* dat, size_t count, const int fn = 0 );
508 
509  ModbusRTU::mbErrCode real_read_it( RegMap& rmap, RegMap::iterator& it, ModbusRTU::ModbusData& val );
510  ModbusRTU::mbErrCode real_bitreg_read_it( std::shared_ptr<BitRegProperty>& bp, ModbusRTU::ModbusData& val );
511  ModbusRTU::mbErrCode real_read_prop( IOProperty* p, ModbusRTU::ModbusData& val );
512 
513  ModbusRTU::mbErrCode real_write_it(RegMap& rmap, RegMap::iterator& it, ModbusRTU::ModbusData* dat, size_t& i, size_t count );
514  ModbusRTU::mbErrCode real_bitreg_write_it( std::shared_ptr<BitRegProperty>& bp, const ModbusRTU::ModbusData val );
515  ModbusRTU::mbErrCode real_write_prop(IOProperty* p, ModbusRTU::ModbusData* dat, size_t& i, size_t count );
516 
517  MBSlave();
518  timeout_t initPause = { 3000 };
519  uniset::uniset_rwmutex mutex_start;
520  std::shared_ptr< ThreadCreator<MBSlave> > thr;
521 
522  std::mutex mutexStartNotify;
523  std::condition_variable startNotifyEvent;
524 
525  PassiveTimer ptHeartBeat;
526  uniset::ObjectId sidHeartBeat = { uniset::DefaultObjectId };
527  long maxHeartBeat = { 10 };
528  IOController::IOStateList::iterator itHeartBeat;
530 
531  IOController::IOStateList::iterator itAskCount;
532  uniset::ObjectId askcount_id = { uniset::DefaultObjectId };
533 
534  IOController::IOStateList::iterator itRespond;
535  uniset::ObjectId respond_id = { uniset::DefaultObjectId };
536  bool respond_invert = { false };
537 
538  PassiveTimer ptTimeout;
539  long askCount = { 0 };
540 
541  std::atomic_bool activated = { false };
542  std::atomic_bool cancelled = { false };
543  timeout_t activateTimeout = { 20000 }; // msec
544  bool pingOK = { false };
545  timeout_t wait_msec = { 3000 };
546  bool force = { false };
548  bool mbregFromID = {0};
549  bool checkMBFunc = {0};
550  bool noMBFuncOptimize = {0}; // флаг отключающий принудительное преобразование функций (0x06->0x10, 0x05->0x0F) см. initItem()
551 
552  int getOptimizeWriteFunction( const int fn ); // функция возвращает оптимизированную функцию (если оптимизация включена)
553 
554  typedef std::unordered_map<int, std::string> FileList;
555  FileList flist;
556  std::string prefix = { "" };
557  std::string prop_prefix = { "" };
558 
559  ModbusRTU::ModbusData buf[ModbusRTU::MAXLENPACKET / 2 + 1];
561  // данные для ответа на запрос 0x2B(43)/0x0E(14)
562  // 'MEI' - modbus encapsulated interface
563  // 'RDI' - read device identification
564  typedef std::unordered_map<int, std::string> MEIValMap;
565  typedef std::unordered_map<int, MEIValMap> MEIObjIDMap;
566  typedef std::unordered_map<int, MEIObjIDMap> MEIDevIDMap;
567 
568  MEIDevIDMap meidev;
569 
570  std::shared_ptr<LogAgregator> loga;
571  std::shared_ptr<DebugStream> mblog;
572  std::shared_ptr<LogServer> logserv;
573  std::string logserv_host = {""};
574  int logserv_port = {0};
575  VMonitor vmon;
576  std::string mbtype = { "" };
577 
578  // ----------------------------------------------------------------------------
579  // TCPServer section..
580  void initTCPClients( UniXML::iterator confnode );
581 
582  timeout_t sessTimeout = { 2000 };
583  timeout_t updateStatTime = { 4000 };
584  ModbusTCPServer::Sessions sess;
585  std::mutex sessMutex;
586  size_t sessMaxNum = { 5 };
587  std::shared_ptr<ModbusTCPServerSlot> tcpserver;
588 
589  struct ClientInfo
590  {
591  ClientInfo(): iaddr(""), respond_s(uniset::DefaultObjectId), invert(false),
592  askCount(0), askcount_s(uniset::DefaultObjectId)
593  {
594  ptTimeout.setTiming(0);
595  }
596 
597  std::string iaddr = { "" };
598 
600  IOController::IOStateList::iterator respond_it;
601  bool invert = { false };
602  PassiveTimer ptTimeout;
603  timeout_t tout = { 2000 };
604 
605  long askCount = { 0 };
606  uniset::ObjectId askcount_s = { uniset::DefaultObjectId };
607  IOController::IOStateList::iterator askcount_it;
608 
609  inline void initIterators( const std::shared_ptr<SMInterface>& shm )
610  {
611  shm->initIterator( respond_it );
612  shm->initIterator( askcount_it );
613  }
614 
615  const std::string getShortInfo() const;
616  };
617 
618  typedef std::unordered_map<std::string, ClientInfo> ClientsMap;
619  ClientsMap cmap;
620 
621  uniset::ObjectId sesscount_id = { uniset::DefaultObjectId };
622  IOController::IOStateList::iterator sesscount_it;
623 
624  std::atomic_bool tcpCancelled = { true };
625  };
626  // --------------------------------------------------------------------------
627 } // end of namespace uniset
628 // -----------------------------------------------------------------------------
629 #endif // _MBSlave_H_
630 // -----------------------------------------------------------------------------