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, } }