cml04-falcon-system/labeler/server/server.go
2024-10-22 17:34:07 +02:00

234 lines
6.7 KiB
Go

package server
import (
"bytes"
"context"
"encoding/binary"
"errors"
"net"
"strconv"
"strings"
"git.espin.casa/albert/cml04-falcon-system/internal/types"
"github.com/google/uuid"
"git.espin.casa/albert/cml04-falcon-system/labeler/service"
"git.espin.casa/albert/logger"
)
type Header struct {
MessageLength int16
TelegramID int16
SequenceCounter int16
Flags int16
TimeStamp [8]int16
}
type LabelData struct {
Header Header
DateTime [23]byte `json:"date_time"`
L2PackageId int32 `json:"l2_package_id"`
L3PackageId [6]byte `json:"l3_package_id"`
ProductionOrderNo int32 `json:"production_order_no"`
CustomerOrderNo int32 `json:"customer_order_no"`
CustomerName [30]byte `json:"customer_name"`
LogoCode [3]byte `json:"logo_code"`
SteelGrade [15]byte `json:"steel_grade"`
MaterialCode [18]byte `json:"material_code"`
HeatId [10]byte `json:"heat_id"`
SectionType [20]byte `json:"section_type"`
PackageDimensions [3]float32 `json:"package_dimensions"`
PackageWeight float32 `json:"package_weight"`
SectionDimensions [3]float32 `json:"section_dimensions"`
NumberSections int32 `json:"number_sections"`
NumberLayers int32 `json:"number_layers"`
NumberSectionsInLayer int32 `json:"number_sections_last_layer"`
FreeTxtLp [180]byte `json:"free_txt_lp"`
}
func cleanNonPrintable(input []byte) string {
var result strings.Builder
for _, b := range input {
if strconv.IsPrint(rune(b)) {
result.WriteByte(b)
}
}
return result.String()
}
func (pmd *LabelData) DateTimeAsString() string {
s := cleanNonPrintable(pmd.DateTime[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) L3PackageIdAsString() string {
s := cleanNonPrintable(pmd.L3PackageId[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) CustomerNameAsString() string {
s := cleanNonPrintable(pmd.CustomerName[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) LogoCodeAsString() string {
s := cleanNonPrintable(pmd.LogoCode[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) SteelGradeAsString() string {
s := cleanNonPrintable(pmd.SteelGrade[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) MaterialCodeAsString() string {
s := cleanNonPrintable(pmd.MaterialCode[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) HeatIdAsString() string {
s := cleanNonPrintable(pmd.HeatId[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) SectionTypeAsString() string {
s := cleanNonPrintable(pmd.SectionType[:])
return strings.TrimRight(string(s), "\x00")
}
func (pmd *LabelData) FreeTxtLpAsString() string {
s := cleanNonPrintable(pmd.FreeTxtLp[:])
return strings.TrimRight(string(s), "\x00")
}
type Server struct {
address string
log logger.LoggerAdapter
svc service.Service
}
type Client struct {
conn net.Conn
log logger.LoggerAdapter
svc service.Service
}
func (client *Client) handleRequest() {
// telegram bytes buffer holder 4096 maximun telegram size defined by SMS
telegramBytes := bytes.NewBuffer(make([]byte, 4096))
for {
pl, err := client.conn.Read(telegramBytes.Bytes())
if err != nil {
client.log.Error("reading telegram failed", err, logger.LogFields{})
return
}
// interpret first 24 bytes as header
header := &Header{}
headerReader := bytes.NewReader(telegramBytes.Bytes()[:24])
if err := binary.Read(headerReader, binary.LittleEndian, header); err != nil {
client.log.Error("interpreting telegram header failed", err, logger.LogFields{})
return
}
// switch telegram id
switch header.TelegramID {
// handle telegram data
case 6500:
if header.MessageLength != int16(pl) {
client.log.Error("message length mismatch telegram defined length", errors.New(""), logger.LogFields{
"telegram_defined_length": header.MessageLength,
"received_length": pl,
})
return
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// read telegram data
telegramData := &LabelData{}
if err := binary.Read(telegramBytes, binary.LittleEndian, telegramData); err != nil {
client.log.Error("reading telegram content failed", err, logger.LogFields{})
return
}
if err := client.handlePublishPrintData(ctx, telegramData); err != nil {
client.log.Error("publish print data failed", err, logger.LogFields{})
return
}
default:
}
}
}
func (client *Client) handlePublishPrintData(ctx context.Context, data *LabelData) error {
// set label data
label := &types.Label{
ID: uuid.NewString(),
DateTime: data.DateTimeAsString(),
L2PackageId: int16(data.L2PackageId),
L3PackageId: data.L3PackageIdAsString(),
ProductionOrderNo: data.ProductionOrderNo,
CustomerOrderNo: data.CustomerOrderNo,
CustomerName: data.CustomerNameAsString(),
LogoCode: data.LogoCodeAsString(),
SteelGrade: data.SteelGradeAsString(),
MaterialCode: data.MaterialCodeAsString(),
HeatId: data.HeatIdAsString(),
SectionType: data.SectionTypeAsString(),
PackageDimenA: data.PackageDimensions[0],
PackageDimenB: data.PackageDimensions[1],
PackageDimenC: data.PackageDimensions[2],
PackageWeight: data.PackageWeight,
SectionDimenA: data.SectionDimensions[0],
SectionDimenB: data.SectionDimensions[1],
SectionDimenC: data.SectionDimensions[2],
NumberSections: data.NumberSections,
NumberLayers: data.NumberLayers,
NumberSectionsInLayer: data.NumberSectionsInLayer,
FreeTxtLp: data.FreeTxtLpAsString(),
}
// store new label data received
if err := client.svc.StoreLabelData(ctx, label); err != nil {
return err
}
// publish new label data event
if err := client.svc.PublishNewLabelData(ctx, label.ID); err != nil {
return err
}
return nil
}
func (server *Server) Run() error {
// create tcp listener
listener, err := net.Listen("tcp", server.address)
if err != nil {
return err
}
// close on exit
defer listener.Close()
// accept incoming connections
for {
// accept connection
conn, err := listener.Accept()
if err != nil {
return err
}
// create new client
client := &Client{
conn: conn,
log: server.log,
svc: server.svc,
}
// handle client
go client.handleRequest()
}
}
func New(address string, svc service.Service, log logger.LoggerAdapter) *Server {
return &Server{
address: address,
svc: svc,
log: log,
}
}