Go in industrial projects

Does anyone intrested in implementation of industrial communication protocols in go?

It can be used for automation projects, web based scadas, data acquisition systems, iot devices etc.

I have some experience and want to start an open source project.

go ahead and start doing it :slight_smile: It’s easier to find contributors to an existing project

Can i take some architectural ideas from you ? :slight_smile:

yeah, what do you need to know?

Thank you, i have a couple of industrial communication clients. i want to collect them in a library. Is this a good practice?

package main

import "fmt"

type Client interface {
	Connect()
}

type TcpClient struct {
	IpAddress string
	Port      int
}

type ModbusTcpClient struct {
	TcpClient
	UnitIndentifier int
}

func (c ModbusTcpClient) Connect() {
	fmt.Println("connect method for modbus tcp client", c)
}

type IsoOnTcpClient struct {
	TcpClient
	Rack int
	Slot int
}

func (c IsoOnTcpClient) Connect() {
	fmt.Println("connect method for iso on tcp client", c)
}

func NewModbusTcpClient(ipAddress string, port, unitIdentifier int) Client {
	c := ModbusTcpClient{
		UnitIndentifier: unitIdentifier,
	}
	c.IpAddress = ipAddress
	c.Port = port
	return c
}

func NewIsoOnTcpClient(ipAddress string, port, rack, slot int) Client {
	c := IsoOnTcpClient{
		Rack: rack,
		Slot: slot,
	}
	c.IpAddress = ipAddress
	c.Port = port
	return c
}

func main() {
	mtc := NewModbusTcpClient("1.2.3.4", 5, 6)
	mtc.Connect()

	iotc := NewIsoOnTcpClient("7.8.9.10", 11, 12, 13)
	iotc.Connect()
}

looks good for me, at least from what I can see.

I think the following (composite pattern) will be unnecessary since there is only two clients and eight different functions.

package main

import "fmt"

const (
	ModbusTcpClient = 1
	IsoOnTcpClient  = 2
)

type Connector interface {
	Connect()
}

type TcpConnector struct {
	IpAddress string
	Port      int
}

//iso on tcp
type IsoOnTcpConnector struct {
	TcpConnector
	Rack int
	Slot int
}

func (c *IsoOnTcpConnector) Connect() {
	fmt.Println("connect method for iso on tcp client")
}

//modbus tcp
type ModbusTcpConnector struct {
	TcpConnector
	UnitIdentifier int
}

func (c *ModbusTcpConnector) Connect() {
	fmt.Println("connect method for modbus tcp client")
}

type Configuration struct {
	Type           int
	IpAddress      string
	Port           int
	UnitIdentifier int
	Rack           int
	Slot           int
}

type Client struct {
	Connector
}

func New(c *Configuration) *Client {
	switch c.Type {
	case ModbusTcpClient:
		mtc := &ModbusTcpConnector{
			UnitIdentifier: c.UnitIdentifier,
		}
		mtc.IpAddress = c.IpAddress
		mtc.Port = c.Port
		return &Client{
			mtc,
		}
	case IsoOnTcpClient:
		iotc := &IsoOnTcpConnector{
			Rack: c.Rack,
			Slot: c.Slot,
		}
		iotc.IpAddress = c.IpAddress
		iotc.Port = c.Port
		return &Client{
			iotc,
		}
	default:
		return nil
	}
}

func main() {
	mtc := New(&Configuration{
		Type:           ModbusTcpClient,
		IpAddress:      "1.2.3.4",
		Port:           502,
		UnitIdentifier: 0,
	})
	mtc.Connect()

	iotc := New(&Configuration{
		Type:      IsoOnTcpClient,
		IpAddress: "1.2.3.4",
		Port:      102,
		Rack:      0,
		Slot:      1,
	})
	iotc.Connect()
}

the question is if other clients will have similar configuration. You should follow open-closed principle. Right now, when a new type will come you have to edit New(c) function.

I’d keep them separate: NewModbusTcp() and NewIsoOnTcpClient(). What they really have in common is the Connect() function but right now I cannot see any use case for Connector interface so I’d drop it. At least for now.

Thank you, i understand.