unit Unitmodbus;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, strUtils;

type
  TForm_modbus = class(TForm)
    Bytestyle: TPanel;
    LabelCom: TLabel;
    Comboxcom: TComboBox;
    ButtonOpen: TButton;
    ButtonClose: TButton;
    Labuser: TLabel;
    Edituser: TEdit;
    TimerRead: TTimer;
    TimerWrite: TTimer;
    Labnode: TLabel;
    Combonode: TComboBox;
    Labread01: TLabel;
    Labread02: TLabel;
    EditRe01: TEdit;
    Labread03: TLabel;
    EditRe02: TEdit;
    Labread04: TLabel;
    EditRe03: TEdit;
    ButtonRead: TButton;
    Labread05: TLabel;
    EditRe04: TEdit;
    Labread06: TLabel;
    EditRe05: TEdit;
    Labread07: TLabel;
    EditRe06: TEdit;
    Labwrite01: TLabel;
    Labwrite02: TLabel;
    EditWr01: TEdit;
    Labwrite03: TLabel;
    EditWr02: TEdit;
    Labwrite04: TLabel;
    EditWr03: TEdit;
    ButtonWtire: TButton;
    Labwrite05: TLabel;
    EditWr04: TEdit;
    Labwrite06: TLabel;
    EditWr05: TEdit;
    Labwrite07: TLabel;
    EditWr06: TEdit;
    LabBaud: TLabel;
    CombBaudRate: TComboBox;
    LabBit01: TLabel;
    LabBit02: TLabel;
    EditbitAddr: TEdit;
    BtnbitWrite: TButton;
    LabBit03: TLabel;
    EditBitvalue: TEdit;
    BtnSet: TButton;
    BtnReset: TButton;
    BtnbitRead: TButton;
    Labcunchu: TLabel;
    OptionWord01: TRadioButton;
    OptionWord02: TRadioButton;
    procedure ButtonOpenClick(Sender: TObject);
    procedure ButtonCloseClick(Sender: TObject);
    procedure EditRe01KeyPress(Sender: TObject; var Key: Char);
    procedure ReadaddressrKeyPress(Sender: TObject; var Key: Char);
    procedure ButtonReadClick(Sender: TObject);
    procedure WMNCHitTest(var Message: TWMNCHitTest);
    message WM_NCHITTEST;    
    procedure TimerReadTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure TimerWriteTimer(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure ButtonWtireClick(Sender: TObject);
    procedure BtnbitWriteClick(Sender: TObject);
    procedure BtnSetClick(Sender: TObject);
    procedure BtnResetClick(Sender: TObject);
    procedure BtnbitReadClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form_modbus: TForm_modbus;
  //定义一个发生数据区的控件数组   
  StartTime,EndTime:Int64;
  modbuson,modbusInt,ComPort,Readtrue:LongInt;
  SendInt:array [0..5] of LongInt;

  //串口控制函数
  function mbrtuSetDelay(value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuComOpen(nport,BaudRate,DataBits:LongInt; Parity:Pchar; StopBits:LongInt; User:Pchar):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuComClose(nport:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuComTrue(nport:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuComWork(nport:LongInt):LongInt;stdcall; external'modbus_rtu.dll';

  //标准modbus函数
  function mbrtufcn01(nport,node,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn02(nport,node,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn03(nport,node,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn04(nport,node,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn05(nport,node,address,value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn06(nport,node,address,value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn15(nport,node,address,Count:LongInt;TxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn16(nport,node,address,Count:LongInt;TxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';

  //延伸modbus函数
  function mbrtufcn03DInt(nport,node,regular,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn03Float(nport,node,regular,address,Count:LongInt; RxdBuffer:PSingle):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn04DInt(nport,node,regular,address,Count:LongInt; RxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn04Float(nport,node,regular,address,Count:LongInt; RxdBuffer:PSingle):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn16DInt(nport,node,regular,address,Count:LongInt;TxdBuffer:PlongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtufcn16Float(nport,node,regular,address,Count:LongInt;TxdBuffer:PSingle):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuWordBitWrite(nport,node,address,Bit,value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function mbrtuWordBitSetReset(nport,node,address,Bit:LongInt):LongInt;stdcall; external'modbus_rtu.dll';

  //混合读写适用函数
  function DecBitBin(value,Bitaddress:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function Int32ToInt_16h(value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function Int32ToInt_16l(value:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function Int16ToInt32(valueH,valueL:LongInt):LongInt;stdcall; external'modbus_rtu.dll';
  function Float32ToInt_16h(value:Single):LongInt;stdcall; external'modbus_rtu.dll';
  function Float32ToInt_16l(value:Single):LongInt;stdcall; external'modbus_rtu.dll';
  function Int16ToFloat32(valueH,valueL:LongInt):Single;stdcall; external'modbus_rtu.dll';

implementation

{$R *.dfm}

procedure TForm_modbus.WMNCHitTest(var Message: TWMNCHitTest);
begin
  inherited;
  case Message.Result of
    //htCaption,
    htBorder,
    htTop,
    htTopLeft,
    htTopRight,
    htLeft,
    htRight,
    htBottom,
    htBottomLeft,
    htBottomRight:
      Message.Result:=htNoWhere;
  end;
end;

procedure TForm_modbus.FormCreate(Sender: TObject);
begin
  ComPort:=1;
  modbuson:=-1;
  modbusInt:=-1;
  Readtrue:=-1;
end;

procedure TForm_modbus.EditRe01KeyPress(Sender: TObject; var Key: Char);
begin
  if not (Key in ['0'..'9', '.','-',char(8)]) then
  begin
    Key:=#0;
  end;
end;

procedure TForm_modbus.ReadaddressrKeyPress(Sender: TObject;
  var Key: Char);
begin
  if not (Key in ['0'..'9',char(8)]) then
  begin
    Key:=#0;
  end;
end;

procedure TForm_modbus.ButtonOpenClick(Sender: TObject);
var
  k,nodeI,ReadInt,BaudRate:longint;
  UserP,ParitP:pchar;
  Userstr,Paritstr:string;
begin
  ComPort:=strtoint(Rightstr(Comboxcom.Text,1));
  try
    UserP:=strAlloc(20);
    ParitP:=strAlloc(4);
    //未注册用户试用只能使用用户名：wjun_2008;
    Userstr:=Edituser.Text;
    //e表示偶校验
    Paritstr:='e';
    UserP:=strPCopy(UserP,Userstr);
    ParitP:=strPCopy(ParitP,Paritstr);
    if mbrtuComTrue(ComPort)=-1 then
    begin
      //设定DLL函数的超时参数16~80 默认值32
      mbrtuSetDelay(32);
      //将DLL用户名及端口号传入打开串口，串口设定9600,8,e,1
      BaudRate:=StrToInt(CombBaudRate.Text);
      k:=mbrtuComOpen(ComPort,BaudRate,8,ParitP,1,UserP);
      if k=1 then                          //k=-1 表示串口打开失败，可能串口不存在或已被占用
        Form_modbus.Caption:=
          '施耐得modbus rtu协议通讯DEMO----DLL已注册'
      else if  k=0 then
        Form_modbus.Caption:=
          '施耐得modbus rtu协议通讯DEMO----DLL未注册';
     if mbrtuComTrue(ComPort)=1 then
     begin
      Comboxcom.Enabled:=False;
      Combonode.Enabled:=False;
      nodeI:=StrToInt(Combonode.Text); 
      modbuson:=-1;
     end;
    end;
  finally
    StrDispose(UserP);
    StrDispose(ParitP);
  end;  
end;

procedure TForm_modbus.ButtonCloseClick(Sender: TObject);
begin
  if mbrtuComTrue(ComPort)=1 then
  begin
    Readtrue := -1;
    ButtonRead.Caption:='读PLC值';
    TimerRead.Enabled:=False; 
    if mbrtuComClose(ComPort)=-1 then Exit;           //关闭串口=-1表示失败
    Comboxcom.Enabled:=True;
    Combonode.Enabled:=True;
    Form_modbus.Caption:='施耐得modbus rtu协议通讯DEMO';  
  end;
end;

//本例程的读写形式，仅仅是种示例，只要掌握PLC的读和写命令不能同时
//并且应确保程序关闭时，串口必须关闭才能正常释放DLL

procedure TForm_modbus.ButtonReadClick(Sender: TObject);
begin
  if Readtrue = 1 then
  begin
    Readtrue := -1;
    ButtonRead.Caption:='读PLC值';
    TimerRead.Enabled:=False;
  end
  else begin
    if mbrtuComTrue(ComPort)=1 then
    begin
      Readtrue := 1;
      ButtonRead.Caption:='停止读值';
      TimerRead.Enabled:=True;
    end;
  end;     
end;

procedure TForm_modbus.ButtonWtireClick(Sender: TObject);
begin
  if (mbrtuComTrue(ComPort)=1) and (modbuson = -1) then
  begin
    modbuson:=1;
    TimerRead.Enabled:=False;
    modbusInt:=1;          //选择PLC通信命令
    TimerWrite.Enabled:=True;  
  end;
end;

procedure TForm_modbus.BtnbitWriteClick(Sender: TObject);
begin
  if (mbrtuComTrue(ComPort)=1) and (modbuson = -1) then
  begin
    modbuson:=1;
    TimerRead.Enabled:=False;
    modbusInt:=2;          //选择PLC通信命令
    TimerWrite.Enabled:=True;
  end;
end;

procedure TForm_modbus.BtnSetClick(Sender: TObject);
begin
  if (mbrtuComTrue(ComPort)=1) and (modbuson = -1) then
  begin
    modbuson:=1;
    TimerRead.Enabled:=False;
    modbusInt:=3;          //选择PLC通信命令
    TimerWrite.Enabled:=True;
  end;
end;

procedure TForm_modbus.BtnResetClick(Sender: TObject);
begin
  if (mbrtuComTrue(ComPort)=1) and (modbuson = -1) then
  begin
    modbuson:=1;
    TimerRead.Enabled:=False;
    modbusInt:=4;          //选择PLC通信命令
    TimerWrite.Enabled:=True;
  end;
end;

procedure TForm_modbus.BtnbitReadClick(Sender: TObject);
begin
  if (mbrtuComTrue(ComPort)=1) and (modbuson = -1) then
  begin
    modbuson:=1;
    TimerRead.Enabled:=False;
    modbusInt:=5;          //选择PLC通信命令
    TimerWrite.Enabled:=True;
  end;
end;

procedure TForm_modbus.TimerReadTimer(Sender: TObject);
var
  nodeI,addrI,ReadInt:LongInt;
  Element:pchar;
  buffer:array [0..1023] of LongInt;     //定义一个数据接收缓冲区
  tempP:PlongInt;
begin
  TimerRead.Enabled:=False;
  if (modbuson = 1) or (mbrtuComTrue(ComPort) <> 1) or (Readtrue = -1) then  Exit;
  //执行循环读取 
  try
    nodeI:=StrToInt(Combonode.Text);
    addrI:=0;             //modbus地址从40001开始，偏移地址0-7,8个存储器值
    tempP:=@buffer;
    ReadInt:=mbrtufcn03(ComPort,nodeI,addrI,8,tempP);         //读取字元件值 
    if ReadInt = 8 then
    begin
      EditRe01.Text:=IntToStr(buffer[0]);
      EditRe02.Text:=IntToStr(buffer[1]);
      if OptionWord01.Checked then   //不同品牌的下位机在双字和浮点的数据存储上有所不同，有高位在前和低位在前之分
        EditRe03.Text:=IntToStr(Int16ToInt32(buffer[3],buffer[2]))
      else
        EditRe03.Text:=IntToStr(Int16ToInt32(buffer[2],buffer[3]));
      EditRe04.Text:=IntToStr(buffer[4]);
      EditRe05.Text:=IntToStr(buffer[5]);
      if OptionWord01.Checked then   //不同品牌的下位机在双字和浮点的数据存储上有所不同，有高位在前和低位在前之分
        EditRe06.Text:=FloatToStrF(Int16ToFloat32(buffer[7],buffer[6]),ffNumber,12,3)
      else
        EditRe06.Text:=FloatToStrF(Int16ToFloat32(buffer[6],buffer[7]),ffNumber,12,3);
    end;
  except
  end;
  if (modbuson = -1) and (Readtrue=1) then TimerRead.Enabled:=True;
end;

procedure TForm_modbus.TimerWriteTimer(Sender: TObject);
var
  SendInt:SmallInt;
  SendFloat:Single;
  nodeI,addrI,ReadInt,SendDint:LongInt;
  buffer:array [0..1023] of LongInt;     //定义一个数据接收缓冲区
  tempP:PlongInt;
begin
  TimerWrite.Enabled:=False;
  case modbusInt of
    0:Close;                             //(先关闭串口再延时关闭程序)确保在循环读取时可以稳定关闭程序
    1:try
        //混合写16(字)、32位整数及浮点数(双字)，但一次最多写125个字
        nodeI:=StrToInt(Combonode.Text);
        addrI:=0;             //modbus地址从40001开始，偏移地址0-7,8个存储器值
        tempP:=@buffer;
        //将混合的数据转换成16位整数
        SendInt:=StrToInt(EditWr01.Text);    //16位整数
        buffer[0]:=SendInt;
        SendInt:=StrToInt(EditWr02.Text);    //16位整数
        buffer[1]:=SendInt;
        SendDint:=StrToInt(EditWr03.Text);   //32位整数
        if OptionWord01.Checked then
        begin
          buffer[2]:=Int32ToInt_16l(SendDint);
          buffer[3]:=Int32ToInt_16h(SendDint);
        end
        else begin
          buffer[2]:=Int32ToInt_16h(SendDint);
          buffer[3]:=Int32ToInt_16l(SendDint);
        end;
        SendInt:=StrToInt(EditWr04.Text);    //16位整数
        buffer[4]:=SendInt;
        SendInt:=StrToInt(EditWr05.Text);    //16位整数
        buffer[5]:=SendInt;
        SendFloat:=StrToFloat(EditWr06.Text);  //32位浮点数
        if OptionWord01.Checked then
        begin
          buffer[6]:=Float32ToInt_16l(SendFloat);
          buffer[7]:=Float32ToInt_16h(SendFloat);
        end
        else begin
          buffer[6]:=Float32ToInt_16h(SendFloat);
          buffer[7]:=Float32ToInt_16l(SendFloat);
        end;
        //将混合的数据转换成16位整数
        mbrtufcn16(ComPort,nodeI,addrI,8,tempP);   //按16位整数写入PLC
      except
      end;
    2:try
        nodeI:=StrToInt(Combonode.Text);
        addrI:=StrToInt(EditbitAddr.Text);        //逻辑线圈modbus地址范围00001~0XXXX 函数调用按偏移地址从0开始
        tempP:=@buffer;
        //将混合的数据转换成16位整数
        SendInt:=StrToInt(EditBitvalue.Text);     //只能是0或者1
        buffer[0]:=SendInt;
        mbrtufcn15(ComPort,nodeI,addrI,1,tempP);  //对逻辑线圈进行多位模式写
      except
      end;
    3:try
        nodeI:=StrToInt(Combonode.Text);
        addrI:=StrToInt(EditbitAddr.Text);        //逻辑线圈modbus地址范围00001~0XXXX 函数调用按偏移地址从0开始
        mbrtufcn05(ComPort,nodeI,addrI,1);
      except
      end;
    4:try
        nodeI:=StrToInt(Combonode.Text);
        addrI:=StrToInt(EditbitAddr.Text);        //逻辑线圈modbus地址范围00001~0XXXX 函数调用按偏移地址从0开始
        mbrtufcn05(ComPort,nodeI,addrI,0);
      except
      end;
    5:try
        nodeI:=StrToInt(Combonode.Text);
        addrI:=StrToInt(EditbitAddr.Text);        //逻辑线圈modbus地址范围00001~0XXXX 函数调用按偏移地址从0开始
        tempP:=@buffer;
        if mbrtufcn01(ComPort,nodeI,addrI,1,tempP) = 1 then
          EditBitvalue.Text:=IntToStr(buffer[0]);
      except
      end;
  end;
  modbuson:=-1;
  if Readtrue = 1 then TimerRead.Enabled:=True;  
end;

procedure TForm_modbus.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  if mbrtuComTrue(ComPort)= 1 then
  begin
    mbrtuComClose(ComPort);
    modbusInt:=0;
    TimerWrite.Enabled:=True;
    CanClose:=False;
  end;  
end;  

end.
