OpenPLC Runtime项目 Modbus.cpp 详细分析

OpenPLC Runtime项目 Modbus.cpp 文件分析

简述

简述描述了 Modbus.cpp 自身定位,基本概念(我是谁)。在 Openplc Runtime 项目中的作用,服务于哪些模块(我去哪)。

文件注释:Modbus.cpp 具有 OpenPLC 支持的所有 Modbus TCP 功能。

Modbus 基本概念:Modbus 是一种常用的串行通信协议,被广泛应用于工业自动化领域。

定义:由Modicon公司,现施耐德电气 Schineider Electric ,于1979年开发的一种串行通信协议。它最初设计用于可编程逻辑控制器(PLC)之间的通信,但现已成为工业领域通信协议的业界标准,并广泛应用于工业电子设备之间的连接。

核心概念:

  1. 开放式协议,免费可用。
  2. 串行通信,支持 RS-232、RS-485、还支持以太网,TCP/IP。
  3. 主/从架构,主节点(Master)负责启动,从(Slave)节点根据命令执行相应的操作并返回结果。
  4. 功能码,Modbus 协议定义了一系列功能码,用于指定设备执行不同的操作。
    1. 读取线圈状态
    2. 读取输入状态/离散输入,
    3. 读取保持寄存器
    4. 读取输入寄存器
    5. 写单个线圈
    6. 写单个寄存器
  5. 支持多种数据格式,
    1. Coil,位,线圈
    2. Discrete Inputs,输入状态,离散输入
    3. Holding Registers,保持寄存器
    4. Input Registers,输入寄存器。

依赖

依赖描述了 Modbus 基于哪些服务层,才能实现自身的功能(我从哪来)。
本节内容主要源于 Modbus.cpp 代码文件。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#include "ladder.h"
#include <string.h>

#include "debug.h"
  • ladder.h 作为 OpenPLC Runtime 项目的公共头文件。
  • stdio.h、stdlib.h、string.h 常用工具头文件。
  • unistd.h 提供操作系统底层功能的访问接口。
  • pthread.h 提供多线程支持,锁。
  • debug.h 提供了调试用的接口声明,接口实现在 debug.cpp ,由 .st 文件生成。

实现

实现本节描述了 Modbus 自身功能的实现思路和原理(为什么来)。

本节内容主要源于 Modbus.cpp 代码文件。

宏、全局变量
#define MAX_DISCRETE_INPUT              8192
#define MAX_COILS                       8192
#define MAX_HOLD_REGS                   8192
#define MAX_INP_REGS                    1024

#define MIN_16B_RANGE                   1024
#define MAX_16B_RANGE                   2047
#define MIN_32B_RANGE                   2048
#define MAX_32B_RANGE                   4095
#define MIN_64B_RANGE                   4096
#define MAX_64B_RANGE                   8191

下面常量均带有 MAX 开头。

用于设置缓冲区长度,以及遍历的结束条件。

  • MAX_DISCRETE_INPUT 离散输入个数最大值,用于设置离散输入缓冲区长度,以及遍历离散输入缓冲区。
  • MAX_COILS 线圈个数最大值,用于设置线圈缓冲区最大值,以及遍历线圈缓冲区。
  • MAX_HOLD_REGS 保持寄存器个数最大值,用于设置保持寄存器缓冲区长度,以及遍历保持寄存器缓冲区。
  • MAX_INP_REGS 输入寄存器个数最大值,用于设置输入寄存器缓冲区长度,以及遍历输入寄存器缓冲区。

下面常量均带有 RANGE 结尾。

用于 IO 时,作为过滤条件。也用于遍历缓冲区时,作为过滤条件。

  • MIN_16B_RANGE、MAX_16B_RANGE,限制 16 位的范围。1024-2047,长度为 1024 位,单位为 16B,存在 64 个 16 B。
  • MIN_32B_RANGE、MAX_32B_RANGE,限制 32 位的范围。2048-4095,长度为 2048 位,单位为 32B,存在 64 个 32 B。
  • MIN_64B_RANGE、MAX_64B_RANGE,限制 64 位的范围。4096-8191,长度为 4096 位,单位为 64B,存在 64 个 64 B。

下面常量是 Modbus 定义的状态码,功能码。

检查 buffer 的第 7 个字节是否等于功能码(见图),根据功能码调用相应的函数

Modbus.cpp processModbusMessage 函数。

mobus_功能码判断ifelse
#define MB_FC_NONE                      0
#define MB_FC_READ_COILS                1
#define MB_FC_READ_INPUTS               2
#define MB_FC_READ_HOLDING_REGISTERS    3
#define MB_FC_READ_INPUT_REGISTERS      4
#define MB_FC_WRITE_COIL                5
#define MB_FC_WRITE_REGISTER            6
#define MB_FC_WRITE_MULTIPLE_COILS      15
#define MB_FC_WRITE_MULTIPLE_REGISTERS  16
#define MB_FC_DEBUG_INFO                0x41 // Request debug variables count
#define MB_FC_DEBUG_SET                 0x42 // Debug set trace (force variable)
#define MB_FC_DEBUG_GET                 0x43 // Debug get trace (read variables)
#define MB_FC_DEBUG_GET_LIST            0x44 // Debug get trace list (read list of variables)
#define MB_FC_DEBUG_GET_MD5             0x45 // Debug get current program MD5
#define MB_FC_ERROR                     255
  • MB_FC_NONE,空
  • MB_FC_READ_COILS ,读线圈
  • MB_FC_READ_INPUTS,读离散输入
  • MB_FC_READ_HOLDING_REGISTERS,读保持寄存器
  • MB_FC_READ_INPUT_REGISTERS,读输入寄存器
  • MB_FC_WRITE_COIL,写线圈
  • MB_FC_WRITE_REGISTER,写寄存器
  • MB_FC_WRITE_MULTIPLE_COILS,写多线圈
  • MB_FC_WRITE_MULTIPLE_REGISTERS,写多寄存器
  • MB_FC_DEBUG_INFO,debug 信息
  • MB_FC_DEBUG_GET,debug get trace,疑问:初识未知?,后来回答:显示索引范围内的变量数据。
  • MB_FC_DEBUG_GET_LIST,debug get trace list,疑问:初识未知?,后来回答:响应包括在所提供的索引列表中指定的变量的跟踪数据。
  • MB_FC_DEBUG_SET,debug set trace,
  • MB_FC_DEBUG_GET_MD5,debug get md5,
  • MB_FC_ERROR,错误码相关。

疑问:FC 是什么的简写?,回答:FC 是 Function 缩写,MB 是 Modbus 缩写。

下面宏均以 ERR 开头。

用于错误码设置函数,见下图。

Modbus.cpp ModbusError 函数。

modbus_错误码函数

函数显示,错误码设置在 buffer 的 index 为 8 的字节,长度设置为 9。
并且 index 为 7 的字节高位为 1。4 和 5 分别为 0 和 3。

// Modbus error codes,Modbus错误码,
// 同样,设置 buffer 的第 7 个字节的最高位为 1,第 8 位存储错误码
#define ERR_NONE                        0
#define ERR_ILLEGAL_FUNCTION            1
#define ERR_ILLEGAL_DATA_ADDRESS        2
#define ERR_ILLEGAL_DATA_VALUE          3
#define ERR_SLAVE_DEVICE_FAILURE        4
#define ERR_SLAVE_DEVICE_BUSY           6
  • ERR_NONE,无错误
  • ERR_ILLEGAL_FUNCTION,无效函数
  • ERR_ILLEGAL_DATA_ADDRESS,无效数据地址,用于地址越界。
  • ERR_ILLEGAL_DATA_VALUE,无效数据值,用于值溢出或不符合约定。
  • ERR_SLAVE_DEVICE_FAILURE,从设备失败。
  • ERR_SLAVE_DEVICE_BUSY,从设备忙。

下面是宏函数,作为工具函数,仅在 Modbus.cpp 中使用。

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

#define lowByte(w) ((unsigned char) ((w) & 0xff))
#define highByte(w) ((unsigned char) ((w) >> 8))
  • bitRead 按位读
  • bitSet 按位写1
  • bitClear 按位置0
  • bitWrite 按位写0或1
  • lowByte,取低8位
  • highByte 取高8位

下面这些宏与 debug 调试相关

#define MB_DEBUG_SUCCESS                 0x7E
#define MB_DEBUG_ERROR_OUT_OF_BOUNDS     0x81
#define MB_DEBUG_ERROR_OUT_OF_MEMORY     0x82
#define SAME_ENDIANNESS                  0
#define REVERSE_ENDIANNESS               1
#define MAX_MB_FRAME                     260
初始化

全局变量声明和分配

// 离散输入缓存区
IEC_BOOL mb_discrete_input[MAX_DISCRETE_INPUT];
// 线圈缓存区
IEC_BOOL mb_coils[MAX_COILS];
// 输入寄存器
IEC_UINT mb_input_regs[MAX_INP_REGS];
// 保持寄存器
IEC_UINT mb_holding_regs[MAX_HOLD_REGS];

int MessageLength;

extern char md5[];
extern unsigned long __tick;

函数实现

函数实现概述
Modbus.cpp 中所有函数分为三种类别:入口函数、核心函数、工具函数。
调用关系依次是外部模块 -> 入口函数 -> 核心函数 -> 工具函数。
入口函数负责转发,接受外部模块的请求,在 OpenPLC rutnime 初始化完成后,进入 Modbus.cpp 文件的所有流量均从入口函数流入(mapUnusedIO 函数调用除外,见下文工具函数说明。),入口函数只有一个 processModbusMessage。经过入口函数的流量,经过功能码的检查,分别转发到不同的核心函数。
核心函数负责业务实现,接受入口函数调用,执行对应于功能码的业务,可能会调用工具函数帮助实现业务。
工具函数负责重复性高的局部功能实现,辅助核心函数完成业务。

入口函数:processModbusMessage

向 server.cpp 提供服务。server.cpp 作为一个服务器,接受来自client的 TCP 请求,将 message 信息发送到此入口函数中。

核心函数:ModbusErrorReadCoilsReadDiscreteInputsReadHoldingRegistersReadInputRegistersWriteCoilWriteRegisterWriteMultipleCoilsWriteMultipleRegistersdebugInfodebugGetTracedebugGetTraceListdebugSetTracedebugGetMd5

核心组件函数均在 processModbusMessage 入口函数中,被调用。通过检测功能码,选择调用哪个核心组件函数。

工具函数:wordmapUnusedIO

用于实现局部功能。

  1. word,将两个字节连接成一个int型,读写线圈、离散输入、输入寄存器、保持寄存器时使用。
  2. mapUnusedIO,该函数将内部NULL OpenPLC缓冲区设置为指向Modbus缓冲区上的有效位置。在 dnp3.cpp dnp3StartServer 函数中调用。
    1. 此函数负责 Modbus 缓冲区的初始化启动,Modbus 初始化属于 OpenPLC runtime 启动时业务。
核心函数阅读

入口函数负责转发,工具函数实现局部功能,它们的角色已然明了,不再详细讨论。
本节对若干核心函数进行阅读讨论,搞清楚实现的具体业务,深刻理解 Modbus 协议内部逻辑。

核心函数:ModbusErrorReadCoilsReadDiscreteInputsReadHoldingRegistersReadInputRegistersWriteCoilWriteRegisterWriteMultipleCoilsWriteMultipleRegistersdebugInfodebugGetTracedebugGetTraceListdebugSetTracedebugGetMd5

  1. 通用
    1. ModbusError
    2. void ModbusError(unsigned char *buffer, int mb_error)

      1. buffer 为缓冲区,缓冲区定义在 server.cpp 中,在函数上下文持续传递。unsigned char buffer[NET_BUFFER_SIZE];
      2. mb_error 为 Modbus 的错误码。
      3. 函数体负责将错误信息写入缓冲区,缓冲区各个字节存储的值存在格式规范,使用长度为 9 字节的 Modbus 响应报文,存储在 buffer 中,第7个字节的最高位设置为 1,表示错误响应,第8个字节存储错误码。
      4. 被调用的代码块
        1. 入口函数检查长度小于 8,if (bufferSize < 8)。设置错误码为 ERR_ILLEGAL_FUNTION,非法函数错误,ModbusError(buffer, ERR_ILLEGAL_FUNCTION);
  2. 线圈
    1. ReadCoils
      1. void ReadCoils(unsigned char *buffer, int bufferSize)

      2. buffer,buffer 为缓冲区,缓冲区定义在 server.cpp 中,在函数上下文持续传递。unsigned char buffer[NET_BUFFER_SIZE];

      3. bufferSize,客户端发送给Server.cpp 的消息长度,messageSize = listenToClient(client_fd, buffer);

      4. 函数体负责,

        1. 非法检查 if (bufferSize < 12)if (ByteDataLength > 255)
        2. 读取 buffer 内容,识别到 start 有效数据存储位置的起始下标、CoilDataLength 线圈数据长度、ByteDataLength 字节数据长度。
        Start = word(buffer[8], buffer[9]);
        CoilDataLength = word(buffer[10], buffer[11]);
        ByteDataLength = CoilDataLength / 8;
        
        1. 加锁和解锁,pthread_mutex_lock(&bufferLock);pthread_mutex_unlock(&bufferLock);
          1. 锁对象 bufferLockmain.cpp 定义,在头文件 ladder.h 额外声明,被 Modbus.cpp 引用。
        2. 加锁临界区负责将 bool_output 内容写入 buffer,按位依次写入。疑问:bool_output 数据谁写入的? 回答:估计是WriteCoil 函数写入的。
           for(int i = 0; i < ByteDataLength ; i++)
           {
              for(int j = 0; j < 8; j++)
              {
                 int position = Start + i * 8 + j;
                 if (position < MAX_COILS)
                 {
                    if (bool_output[position/8][position%8] != NULL)
                    {
                       bitWrite(buffer[9 + i], j, *bool_output[position/8][position%8]);
                    }
                    else
                    {
                       bitWrite(buffer[9 + i], j, 0);
                    }
                 }
              }
           }
        
      5. 被调用的代码块

        1. 入口函数,if(buffer[7] == MB_FC_READ_COILS)检查功能码为 MB_FC_READ_COILS,Modbus 函数读线圈 ,ReadCoils(buffer, bufferSize);
    2. WriteCoil
      1. void WriteCoil(unsigned char *buffer, int bufferSize)

      2. buffer,buffer,buffer 为缓冲区,缓冲区定义在 server.cpp 中,在函数上下文持续传递。unsigned char buffer[NET_BUFFER_SIZE];

      3. bufferSize,客户端发送给Server.cpp 的消息长度,messageSize = listenToClient(client_fd, buffer);

      4. 函数体负责设置 bool_output 特定位为 0 或 1

        1. 非法检查,if (bufferSize < 12)if (Start < MAX_COILS)
        2. 识别起始下标,Start = word(buffer[8], buffer[9]);
        3. 分配写入值 if (word(buffer[10], buffer[11]) > 0) value = 1;
        4. 加锁解锁,pthread_mutex_lock(&bufferLock);pthread_mutex_unlock(&bufferLock);
        5. 临界区中,设置 bool_ouput 特定位的值,
        if (bool_output[Start/8][Start%8] != NULL)
        {
           *bool_output[Start/8][Start%8] = value;
        }
        
      5. 被调用的代码块

        1. 入口函数,if(buffer[7] == MB_FC_WRITE_COIL),Modbus 函数写线圈,WriteCoil(buffer, bufferSize);
    3. WriteMultipleCoils
      1. void WriteMultipleCoils(unsigned char *buffer, int bufferSize)

      2. buffer,buffer,buffer 为缓冲区,缓冲区定义在 server.cpp 中,在函数上下文持续传递。unsigned char buffer[NET_BUFFER_SIZE];
      3. bufferSize,客户端发送给Server.cpp 的消息长度,messageSize = listenToClient(client_fd, buffer);
      4. 函数体负责将 buffer 中有效位循环写入 bool_output 的特定位
        1. 非法检查,if (bufferSize < 12)if ( (bufferSize < (13 + ByteDataLength)) || (buffer[12] != ByteDataLength) )
        2. 识别 Start buffer 中有效位起始下标,CoilDataLength 线圈数据位长度,ByteDataLength 线圈数据字节长度。
        Start = word(buffer[8],buffer[9]);
        CoilDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = CoilDataLength / 8;
        
        1. 按规则,准备响应buffer中特定位, buffer[4] = 0;buffer[5] = 6;
        2. 加锁解锁,pthread_mutex_lock(&bufferLock);pthread_mutex_unlock(&bufferLock);
        3. 临界区负责循环写入 bool_output 的特定位
        for(int i = 0; i < ByteDataLength ; i++)
        {
           for(int j = 0; j < 8; j++)
           {
              int position = Start + i * 8 + j;
              if (position < MAX_COILS)
              {
                 if (bool_output[position/8][position%8] != NULL) 
                    *bool_output[position/8][position%8] = bitRead(buffer[13 + i], j);
              }
           }
        }
        
      5. 被调用代码块,
        1. 入口函数,if(buffer[7] == MB_FC_WRITE_MULTIPLE_COILS)WriteMultipleCoils(buffer, bufferSize);
  3. 离散输入
    1. ReadDiscreteInputs
      1. void ReadDiscreteInputs(unsigned char *buffer, int bufferSize)

      2. buffer,buffer,buffer 为缓冲区,缓冲区定义在 server.cpp 中,在函数上下文持续传递。unsigned char buffer[NET_BUFFER_SIZE];
      3. bufferSize,客户端发送给Server.cpp 的消息长度,messageSize = listenToClient(client_fd, buffer);
      4. 函数体负责循环按位将 bool_input 有效位写入 buffer
        1. 识别局部信息
        Start = word(buffer[8],buffer[9]);
        InputDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = InputDataLength / 8;
        
        1. 加解锁,pthread_mutex_lock(&bufferLock);pthread_mutex_unlock(&bufferLock);
        2. 临界区中循环写入
        for(int i = 0; i < ByteDataLength ; i++)
        {
           for(int j = 0; j < 8; j++)
           {
              int position = Start + i * 8 + j;
              if (position < MAX_DISCRETE_INPUT)
              {
                 if (bool_input[position/8][position%8] != NULL)
                 {
                    bitWrite(buffer[9 + i], j, *bool_input[position/8][position%8]);
                 }
              }
           }
        }
        
      5. 被调用代码块,
        1. 入口函数,if(buffer[7] == MB_FC_READ_INPUTS) ReadDiscreteInputs(buffer, bufferSize);
  4. 寄存器,省略内容重复的部分(buffer、bufferSize参数,加解锁),保留关键内容,便于阅读。
    1. ReadHoldingRegisters
      1. void ReadHoldingRegisters(unsigned char *buffer, int bufferSize)

      2. 函数体负责循环按字节将 int_memorydint_memorylint_memory 内容写入 buffer
        1. 识别局部变量,
        Start = word(buffer[8],buffer[9]);
        WordDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = WordDataLength * 2;
        
        1. 配置响应buffer格式
        buffer[4] = highByte(ByteDataLength + 3);
        buffer[5] = lowByte(ByteDataLength + 3); //Number of bytes after this one
        buffer[8] = ByteDataLength; //Number of bytes of data
        
        1. 临界区负责写入 buffer
        // 16 位内存,每次写入 2 个 8 位。
        //...
        buffer[ 9 + i * 2] = highByte(*int_memory[position - MIN_16B_RANGE]);
        buffer[10 + i * 2] = lowByte(*int_memory[position - MIN_16B_RANGE]);
        // 32 位内存,写入到 buffer 中,每次写入2 个 8位
        // ...
        uint16_t tempValue = (uint16_t)(*dint_memory[(position - MIN_32B_RANGE)/2] >> 16);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        // ...
        uint16_t tempValue = (uint16_t)(*dint_memory[(position - MIN_32B_RANGE)/2] & 0xffff);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        // 64 位内存,每次写入 2 个8位
        //...
        uint16_t tempValue = (uint16_t)(*lint_memory[(position - MIN_64B_RANGE)/4] >> 48);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        //...
        uint16_t tempValue = (uint16_t)((*lint_memory[(position - MIN_64B_RANGE)/4] >> 32) & 0xffff);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        //...
        uint16_t tempValue = (uint16_t)((*lint_memory[(position - MIN_64B_RANGE)/4] >> 16) & 0xffff);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        //...
        uint16_t tempValue = (uint16_t)(*lint_memory[(position - MIN_64B_RANGE)/4] & 0xffff);
        buffer[ 9 + i * 2] = highByte(tempValue);
        buffer[10 + i * 2] = lowByte(tempValue);
        
      3. 被调代码块,
        1. 入口函数,MB_FC_READ_HOLDING_REGISTERS
    2. ReadInputRegisters
      1. void ReadInputRegisters(unsigned char *buffer, int bufferSize)

      2. 函数体负责循环按字节将 int_input 写入 buffer
        1. 识别局部信息
        Start = word(buffer[8],buffer[9]);
        WordDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = WordDataLength * 2;
        
        1. 准备响应格式
        Start = word(buffer[8],buffer[9]);
        WordDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = WordDataLength * 2;
        
        1. 临界区循环写入
        buffer[ 9 + i * 2] = highByte(*int_input[position]);
        buffer[10 + i * 2] = lowByte(*int_input[position]);
        
        int_input,是 unsigned short int 类型数组。
      3. 被调用代码块
        1. 入口函数,MB_FC_READ_INPUT_REGISTERS
    3. WriteRegister,在给定的位置向寄存器写入一个单词。调用者必须持有“ BufferLock”。
      1. void WriteRegister(unsigned char *buffer, int bufferSize)

      2. 函数体负责将 buffer 内容写入 int_memorymb_holding_regsdint_memorylint_memory
        1. 识别局部变量,Start = word(buffer[8],buffer[9])
        2. 临界区写入封装函数调用,mb_error = writeToRegisterWithoutLocking(Start, word(buffer[10], buffer[11]));
        3. int writeToRegisterWithoutLocking(int position, uint16_t value)

          1. position,有效位起始位置。
          2. value,16位数据,需要写入的值。
          3. return int,错误码,反映执行状态。
        // 模拟输出
        *int_output[position] = value;
        // 16 位
        *int_memory[position - MIN_16B_RANGE] = value;
        // 32 位
        mb_holding_regs[position] = value;
        // Overwrite one word of the 32 bit register:
        // Calculate the bit offset of the word in the 32 bit register.
        int bit_offset = (1 - ((position - MIN_32B_RANGE) % 2)) * 16;
        // Mask the word.
        *dint_memory[(position - MIN_32B_RANGE) / 2] &= ~(((uint32_t) 0xffff) << bit_offset);
        // Overwrite the word.
        *dint_memory[(position - MIN_32B_RANGE) / 2] |= ((uint32_t) value) << bit_offset;
        // 64 位
        mb_holding_regs[position] = value;
        // Overwrite one word of the 64 bit register:
        // Calculate the bit offset of the word in the 64 bit register.
        int bit_offset = (3 - ((position - MIN_64B_RANGE) % 4)) * 16;
        // Mask the word.
        *lint_memory[(position - MIN_64B_RANGE) / 4] &= ~(((uint64_t) 0xffff) << bit_offset);
        // Overwrite the word.
        *lint_memory[(position - MIN_64B_RANGE) / 4] |= ((uint64_t) value) << bit_offset;
        
      3. 被调用代码块,
        1. 入口函数,MB_FC_WRITE_COIL
    4. WriteMultipleRegisters
      1. void WriteMultipleRegisters(unsigned char *buffer, int bufferSize)

      2. 函数体负责循环将 buffer 内容写入 int_memorymb_holding_regsdint_memorylint_memory
        1. 识别局部变量
        Start = word(buffer[8],buffer[9]);
        WordDataLength = word(buffer[10],buffer[11]);
        ByteDataLength = WordDataLength * 2;
        
        1. 准备响应格式
        buffer[4] = 0;
        buffer[5] = 6; //Number of bytes after this one.
        
        1. 临界区负责循环写入,
        for(int i = 0; i < WordDataLength; i++)
        {
           int position = Start + i;
           int error = writeToRegisterWithoutLocking(position, word(buffer[13 + i * 2], buffer[14 + i * 2]));
        }
        
        传入 16 位数据,写入到 posistion 下标的位置。
      3. 被调用代码块,
        1. 入口函数,MB_FC_WRITE_MULTIPLE_REGISTERS
  5. 调试
    1. debugInfo,发送DEBUG_INFO功能代码的Modbus响应帧。这个函数为DEBUG_INFO函数代码构造一个Modbus响应帧。响应帧包括PLC程序中定义的变量的数量。
     * Modbus Response Frame (DEBUG_INFO):
     * +-----+-------+-------+
     * | MB  | Count | Count |
     * | FC  |       |       |
     * +-----+-------+-------+
     * |0x41 | High  | Low   |
     * |     | Byte  | Byte  |
     * |     |       |       |
     * +-----+-------+-------+
    
    1. void debugInfo(unsigned char *mb_frame)

    2. 被调用代码,
      1. 入口函数,MB_FC_DEBUG_INFOdebugInfo(buffer);
    3. debugGetTrace,这个函数为DEBUG_GET函数代码构造一个Modbus响应帧。响应帧包括指定索引范围内变量的跟踪数据。
    4. debugGetTraceList,这个函数为DEBUG_GET_LIST函数代码构造一个Modbus响应帧。响应框架包括在所提供的索引列表中指定的变量的跟踪数据。
    5. debugSetTrace,这个函数为DEBUG_SET函数代码构造一个Modbus响应帧。响应帧指示set trace命令是否成功,或者是否有错误,比如索引越界。
    6. debugGetMd5,
posted @ 2024-10-15 08:49  生产队的扛把子  阅读(336)  评论(0)    收藏  举报