github.com/simpleiot/simpleiot@v0.18.3/modbus/pdu_test.go (about)

     1  package modbus
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  )
     8  
     9  func TestPduReadCoils(t *testing.T) {
    10  	regs := Regs{}
    11  	regs.AddCoil(128) // add register 8 for coil 128
    12  	_ = regs.WriteCoil(128, true)
    13  
    14  	pdu := ReadCoils(128, 1)
    15  
    16  	_, resp, err := pdu.ProcessRequest(&regs)
    17  
    18  	if err != nil {
    19  		t.Errorf("Error processing request: %v", err)
    20  	}
    21  
    22  	bits, err := resp.RespReadBits()
    23  
    24  	if err != nil {
    25  		t.Errorf("Error getting bits: %v", err)
    26  	}
    27  
    28  	if len(bits) != 1 {
    29  		t.Errorf("expected 1 bit, got %v", len(bits))
    30  	}
    31  
    32  	if !bits[0] {
    33  		t.Error("Expected high bit")
    34  	}
    35  }
    36  
    37  func TestPduWriteSingleCoil(t *testing.T) {
    38  	regs := Regs{}
    39  	regs.AddCoil(128) // add register 8 for coil 128
    40  	_ = regs.WriteCoil(128, true)
    41  
    42  	pdu := WriteSingleCoil(128, false)
    43  
    44  	_, resp, err := pdu.ProcessRequest(&regs)
    45  
    46  	if err != nil {
    47  		t.Errorf("Error processing request: %v", err)
    48  	}
    49  
    50  	if got, want := resp.FunctionCode, FuncCodeWriteSingleCoil; got != want {
    51  		t.Errorf("got function code %x, want %x", got, want)
    52  	}
    53  
    54  	data := Uint16Array(resp.Data)
    55  	if got, want := data[0], uint16(128); got != want {
    56  		t.Errorf("got address %d, want %d", got, want)
    57  	}
    58  	if got, want := data[1], uint16(0); got != want {
    59  		t.Errorf("got value %d, want %d", got, want)
    60  	}
    61  }
    62  
    63  func TestPduWriteSingleCoilError(t *testing.T) {
    64  	regs := Regs{}
    65  	regs.AddCoil(128) // add register 8 for coil 128
    66  	_ = regs.WriteCoil(128, true)
    67  
    68  	pdu := WriteSingleCoil(64, false)
    69  
    70  	_, resp, err := pdu.ProcessRequest(&regs)
    71  
    72  	if err != nil {
    73  		t.Errorf("Error processing request: %v", err)
    74  	}
    75  
    76  	if got, want := resp.FunctionCode, 0x80|FuncCodeWriteSingleCoil; got != want {
    77  		t.Errorf("got function code %x, want %x", got, want)
    78  	}
    79  	if len(resp.Data) != 1 || resp.Data[0] != byte(ExcIllegalAddress) {
    80  		t.Errorf("got exception code %x, want %x", resp.Data[0], byte(ExcIllegalAddress))
    81  	}
    82  }
    83  
    84  func TestPduReadHoldingRegs(t *testing.T) {
    85  	regs := Regs{}
    86  	regs.AddReg(8, 1)
    87  	_ = regs.WriteReg(8, 0x1234)
    88  
    89  	pdu := ReadHoldingRegs(8, 1)
    90  
    91  	_, resp, err := pdu.ProcessRequest(&regs)
    92  
    93  	if err != nil {
    94  		t.Fatal("Error processing request: ", err)
    95  	}
    96  
    97  	if resp.Data[0] != 2 {
    98  		t.Fatal("expected byte count to be 2")
    99  	}
   100  
   101  	values := Uint16Array(resp.Data[1:])
   102  	if len(values) != 1 {
   103  		t.Fatal("Expected 1 values in response")
   104  	}
   105  
   106  	if values[0] != 0x1234 {
   107  		t.Fatal("wrong value")
   108  	}
   109  }
   110  
   111  func TestProcessRequest(t *testing.T) {
   112  	regs := Regs{}
   113  	regs.AddCoil(128) // add register 8 for coil 128
   114  	_ = regs.WriteCoil(128, true)
   115  	_ = regs.WriteCoil(130, true)
   116  	_ = regs.WriteCoil(132, true)
   117  	regs.AddCoil(144) // add register 9 for coil 144
   118  
   119  	for _, test := range []struct {
   120  		name string
   121  		in   []byte
   122  		out  []byte
   123  	}{
   124  		{"ReadCoils/one", []byte{1, 0, 128, 0, 1}, []byte{1, 1, 1}},
   125  		{"ReadCoils/multiple", []byte{1, 0, 128, 0, 3}, []byte{1, 1, 5}},
   126  		{"ReadCoils/missing", []byte{1, 0, 16, 0, 1}, []byte{0x81, 2}},
   127  		{"ReadDiscreteInputs/one", []byte{2, 0, 128, 0, 1}, []byte{2, 1, 1}},
   128  		{"ReadDiscreteInputs/multiple", []byte{2, 0, 128, 0, 1}, []byte{2, 1, 1}},
   129  		{"ReadDiscreteInputs/one", []byte{2, 0, 128, 0, 3}, []byte{2, 1, 5}},
   130  		{"ReadDiscreteInputs/missing", []byte{2, 0, 16, 0, 1}, []byte{0x82, 2}},
   131  		{"ReadHoldingRegisters/one", []byte{3, 0, 8, 0, 1}, []byte{3, 2, 0, 0x15}},
   132  		{"ReadHoldingRegisters/multiple", []byte{3, 0, 8, 0, 2}, []byte{3, 4, 0, 0x15, 0, 0}},
   133  		{"ReadHoldingRegisters/missing", []byte{3, 0, 7, 0, 2}, []byte{0x83, 2}},
   134  		{"ReadInputRegisters/one", []byte{4, 0, 8, 0, 1}, []byte{4, 2, 0, 0x15}},
   135  		{"ReadInputRegisters/multiple", []byte{4, 0, 8, 0, 2}, []byte{4, 4, 0, 0x15, 0, 0}},
   136  		{"ReadInputRegisters/missing", []byte{4, 0, 7, 0, 2}, []byte{0x84, 2}},
   137  		{"WriteSingleCoil/present", []byte{5, 0, 129, 0xFF, 0}, []byte{5, 0, 129, 0xFF, 0}},
   138  		{"WriteSingleCoil/illegal", []byte{5, 0, 129, 10, 0}, []byte{0x85, 3}},
   139  		{"WriteSingleCoil/missing", []byte{5, 0, 127, 0xFF, 0}, []byte{0x85, 2}},
   140  		{"WriteSingleCoil/readback", []byte{1, 0, 128, 0, 3}, []byte{1, 1, 7}},
   141  		{"WriteMultipleCoils/present", []byte{15, 0, 130, 0, 3, 1, 0x07}, []byte{15, 0, 130, 0, 3}},
   142  		{"WriteMultipleCoils/missing", []byte{15, 0, 120, 0, 3, 1, 0x07}, []byte{0x8F, 2}},
   143  		{"WriteMultipleCoils/readback", []byte{1, 0, 128, 0, 8}, []byte{1, 1, 0b00011111}},
   144  		{"WriteSingleRegister/present", []byte{6, 0, 8, 0, 4}, []byte{6, 0, 8, 0, 4}},
   145  		{"WriteSingleRegister/missing", []byte{6, 0, 6, 0, 4}, []byte{0x86, 2}},
   146  		{"WriteSingleRegister/readback", []byte{3, 0, 8, 0, 1}, []byte{3, 2, 0, 4}},
   147  		{"WriteMultipleRegisters/one", []byte{0x10, 0, 8, 0, 1, 2, 0, 8}, []byte{0x10, 0, 8, 0, 1}},
   148  		{"WriteMultipleRegisters/multiple", []byte{0x10, 0, 8, 0, 2, 4, 0, 8, 10, 15}, []byte{0x10, 0, 8, 0, 2}},
   149  		{"WriteMultipleRegisters/missing", []byte{0x10, 0, 7, 0, 2, 4, 9, 10, 11, 12}, []byte{0x90, 2}},
   150  		{"WriteMultipleRegisters/wronglen", []byte{0x10, 0, 7, 0, 2, 4, 9, 10}, []byte{0x90, 3}},
   151  		{"WriteMultipleRegisters/readback", []byte{3, 0, 8, 0, 2}, []byte{3, 4, 0, 8, 10, 15}},
   152  	} {
   153  		t.Run(test.name, func(t *testing.T) {
   154  			pdu := &PDU{
   155  				FunctionCode: FunctionCode(test.in[0]),
   156  				Data:         test.in[1:],
   157  			}
   158  			_, resp, err := pdu.ProcessRequest(&regs)
   159  
   160  			if err != nil {
   161  				t.Errorf("Error processing request: %v", err)
   162  			}
   163  
   164  			want := PDU{
   165  				FunctionCode: FunctionCode(test.out[0]),
   166  				Data:         test.out[1:],
   167  			}
   168  
   169  			if diff := cmp.Diff(resp, want); diff != "" {
   170  				t.Errorf("unexpected reply: got(-), want(+):\n%s", diff)
   171  			}
   172  			t.Logf("register state: %+v", &regs)
   173  		})
   174  	}
   175  }