diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 33f1332..dd62118 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,14 +4,14 @@ on: push: paths-ignore: - '**/*.yml' - - 'verControl.go' + - 'const.go' - '**/*.txt' - '*.md' - '*.pdf' pull_request: paths-ignore: - '**/*.yml' - - 'verControl.go' + - 'const.go' - '**/*.txt' - '*.md' - '*.pdf' diff --git a/0_test.go b/0_test.go index aefbf72..2cefc36 100644 --- a/0_test.go +++ b/0_test.go @@ -1,16 +1,52 @@ package mpost import ( + "fmt" + "github.com/hard-soft-ware/mpost/enum" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" "testing" "time" ) -func TestName(t *testing.T) { - a := NewCAcceptor(30*time.Second, 30*time.Second) - a.Open("/dev/ttyUSB0", B) +func TestConnect(t *testing.T) { + return - time.Sleep(5 * time.Second) + a := DefAcceptor + a.AddLog( + log.Output(zerolog.ConsoleWriter{ + Out: os.Stdout, + NoColor: false, + TimeFormat: "15:04:05", + }), + "TEST", + false, + ) + + a.AddHook(enum.EventConnected, func(acceptor *CAcceptor, i int) { + fmt.Println("Connect") + + acceptor.SetEnableAcceptance(true) + acceptor.SetEnableBarCodes(true) + acceptor.SetEnableBookmarks(true) + }) + a.AddHook(enum.EventDisconnected, func(acceptor *CAcceptor, i int) { + fmt.Println("Disconnect") + }) + a.AddHook(enum.EventRejected, func(acceptor *CAcceptor, i int) { + fmt.Println("EventRejected") + }) + + a.Open("/dev/ttyUSB0", enum.PowerUpE) + + time.Sleep(2 * time.Second) t.Log(a.GetDeviceSerialNumber()) + t.Log(a.GetBill()) + t.Log(a.GetApplicationPN()) + t.Log(a.GetBootPN()) + t.Log(a.GetDeviceState().String()) + time.Sleep(10 * time.Second) a.Close() } diff --git a/_run/fileGenerator/const.go b/_run/fileGenerator/const.go new file mode 100644 index 0000000..bbbac65 --- /dev/null +++ b/_run/fileGenerator/const.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + "github.com/hard-soft-ware/mpost/_run/fileGenerator/generator" +) + +//////////////////////////////////// + +const constDir = "consts" + +//// + +func Cmd() { + obj := generator.Init("Cmd", constDir+"/cmd.go") + val := obj.NewVal() + + val.Add(0x10, "Omnibus") + val.Add(0x40, "Calibrate") + val.Add(0x50, "FlashDownload") + val.Add(0x60, "Auxiliary") + val.Add(0x70, "Expanded") + + constGenerator(obj, val) + obj.Save(constDir).End() + fmt.Printf("Add: %s %s\n", constDir, obj.Name.Get()) +} + +func CmdAux() { + obj := generator.Init("CmdAux", constDir+"/cmdAux.go") + val := obj.NewVal() + + val.Add(0x00, "SoftwareCRC") + val.Add(0x01, "CashBoxTotal") + val.Add(0x02, "DeviceResets") + val.Add(0x10, "BNFStatus") + val.Add(0x11, "SetBezel") + val.Add(0x0D, "DeviceCapabilities") + val.Add(0x03, "CleDeviceResetsarCashBoxTotal").Delim() + + val.Add(0x04, "AcceptorType") + val.Add(0x0E, "AcceptorApplicationID") + val.Add(0x0F, "AcceptorVariantID") + val.Add(0x05, "AcceptorSerialNumber") + val.Add(0x06, "AcceptorBootPartNumber") + val.Add(0x07, "AcceptorApplicationPartNumber") + val.Add(0x08, "AcceptorVariantName") + val.Add(0x09, "AcceptorVariantPartNumber") + val.Add(0x0A, "AcceptorAuditLifeTimeTotals") + val.Add(0x0B, "AcceptorAuditQPMeasures") + val.Add(0x0C, "AcceptorAuditPerformanceMeasures") + + constGenerator(obj, val) + obj.Save(constDir).End() + fmt.Printf("Add: %s %s\n", constDir, obj.Name.Get()) +} + +func Data() { + obj := generator.Init("Data", constDir+"/data.go") + val := obj.NewVal() + + val.Add(0x02, "STX") // Начало текста + val.Add(0x03, "ETX") // Конец текста + val.Add(0x06, "ACKMask") // Маска подтверждения + + constGenerator(obj, val) + obj.Save(constDir).End() + fmt.Printf("Add: %s %s\n", constDir, obj.Name.Get()) +} diff --git a/_run/fileGenerator/enum.go b/_run/fileGenerator/enum.go new file mode 100644 index 0000000..55b0a34 --- /dev/null +++ b/_run/fileGenerator/enum.go @@ -0,0 +1,187 @@ +package main + +import ( + "fmt" + "github.com/hard-soft-ware/mpost/_run/fileGenerator/generator" +) + +//////////////////////////////////// + +const enumDir = "enum" + +//// + +func Status() { + obj := generator.Init("BNFStatus", enumDir+"/status.go") + + val := []string{ + "Unknown", + "Error", + "OK", + "NotAttached", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func Document() { + obj := generator.Init("Document", enumDir+"/document.go") + + val := []string{ + "None", + "NoValue", + "Bill", + "Barcode", + "Coupon", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func Orientation() { + obj := generator.Init("Orientation", enumDir+"/orientation.go") + + val := []string{ + "RightUp", + "RightDown", + "LeftUp", + "LeftDown", + "UnknownOrientation", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func OrientationControl() { + obj := generator.Init("OrientationControl", enumDir+"/orientation_control.go") + + val := []string{ + "FourWay", + "TwoWay", + "OneWay", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func PowerUp() { + obj := generator.Init("PowerUp", enumDir+"/powerUp.go") + + val := []string{ + "A", + "B", + "C", + "E", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func PupExt() { + obj := generator.Init("PupExt", enumDir+"/pupExt.go") + + val := []string{ + "Return", + "OutOfService", + "Stack", + "StackNoCredit", + "Wait", + "WaitNoCredit", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func State() { + obj := generator.Init("State", enumDir+"/state.go") + + val := []string{ + "Escrow", + "Stacked", + "Returned", + "Rejected", + "Stalled", + "Accepting", + "CalibrateStart", + "Calibrating", + "Connecting", + "Disconnected", + "Downloading", + "DownloadRestart", + "DownloadStart", + "Failed", + "Idling", + "PupEscrow", + "Returning", + "Stacking", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func Bezel() { + obj := generator.Init("Bezel", enumDir+"/bezel.go") + + val := []string{ + "Standard", + "Platform", + "Diagnostic", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} + +func Event() { + obj := generator.Init("Event", enumDir+"/event.go") + + val := []string{ + "_Begin", + "Connected", + "Escrow", + "PUPEscrow", + "Stacked", + "Returned", + "Rejected", + "Cheated", + "StackerFull", + "CalibrateStart", + "CalibrateProgress", + "CalibrateFinish", + "DownloadStart", + "DownloadRestart", + "DownloadProgress", + "DownloadFinish", + "PauseDetected", + "PauseCleared", + "StallDetected", + "StallCleared", + "JamDetected", + "JamCleared", + "PowerUp", + "InvalidCommand", + "CashBoxAttached", + "CashBoxRemoved", + "Disconnected", + "_End", + } + + enumGenerator(obj, val) + obj.Save(enumDir).End() + fmt.Printf("Add: %s %s\n", enumDir, obj.Name.Get()) +} diff --git a/_run/fileGenerator/generator/const.go b/_run/fileGenerator/generator/const.go new file mode 100644 index 0000000..e5b990d --- /dev/null +++ b/_run/fileGenerator/generator/const.go @@ -0,0 +1,8 @@ +package generator + +//###########################################################// + +const ( + hText = "/* This file is automatically generated */" + re = `[^a-zA-Z0-9_]` +) diff --git a/_run/fileGenerator/generator/methods.go b/_run/fileGenerator/generator/methods.go new file mode 100644 index 0000000..eba9170 --- /dev/null +++ b/_run/fileGenerator/generator/methods.go @@ -0,0 +1,72 @@ +package generator + +import ( + "fmt" + "strings" +) + +//###########################################################// + +func (gen *GeneratorObj) NewVal() *GeneratorValueObj { + return generatorValueInit(&gen.name) +} + +//// + +func (gen *GeneratorObj) Len() int { + return gen.buf.Len() +} + +func (gen *GeneratorObj) Write(data []byte) *GeneratorObj { + gen.buf.Write(data) + return gen +} + +func (gen *GeneratorObj) Del(len int) *GeneratorObj { + gen.buf.Truncate(gen.Len() - len) + return gen +} + +// + +func (gen *GeneratorObj) LN() *GeneratorObj { + gen.Write([]byte("\n")) + return gen +} + +func (gen *GeneratorObj) Print(text string) *GeneratorObj { + gen.Write([]byte(text)) + return gen +} + +func (gen *GeneratorObj) PrintLN(text string) *GeneratorObj { + gen.Print(text).LN() + return gen +} + +func (gen *GeneratorObj) Repeat(chat string, size int) *GeneratorObj { + gen.Print(strings.Repeat(chat, size)) + return gen +} + +func (gen *GeneratorObj) Offset(size int) *GeneratorObj { + gen.Repeat("\t", size) + return gen +} + +// + +func (gen *GeneratorObj) String(a any) *GeneratorObj { + gen.Print(fmt.Sprintf("\"%s\"", a)) + return gen +} + +func (gen *GeneratorObj) Hex(a any) *GeneratorObj { + gen.Print(fmt.Sprintf("0x%02x", a)) + return gen +} + +func (gen *GeneratorObj) Number(a any) *GeneratorObj { + gen.Print(fmt.Sprintf("%d", a)) + return gen +} diff --git a/_run/fileGenerator/generator/names.go b/_run/fileGenerator/generator/names.go new file mode 100644 index 0000000..9174c53 --- /dev/null +++ b/_run/fileGenerator/generator/names.go @@ -0,0 +1,153 @@ +package generator + +import ( + "regexp" + "strings" + "unicode" +) + +//###########################################################// + +func (n *GeneratorNameObj) ToTitleCase(s string) string { + s = strings.ReplaceAll(s, "-", " ") + r := regexp.MustCompile(re) + words := strings.Fields(s) + + for i, word := range words { + if len(word) > 0 { + word = r.ReplaceAllString(word, "") + if len(word) > 0 { + words[i] = string(unicode.ToUpper(rune(word[0]))) + word[1:] + } + + } + } + + return strings.Join(words, "") +} + +//// + +func (n *GeneratorNameObj) Get() string { + return n.ToTitleCase(n.gen.name) +} + +func (n *GeneratorNameObj) GetParam(param string) string { + return n.Get() + n.ToTitleCase(param) +} + +func (n *GeneratorNameObj) GetObj() string { + return n.GetParam("Obj") +} + +func (n *GeneratorNameObj) GetMap() string { + return n.GetParam("Map") +} + +func (n *GeneratorNameObj) GetType() string { + return n.GetParam("Type") +} + +func (n *GeneratorNameObj) GetText() string { + return n.GetParam("Text") +} + +// + +func (n *GeneratorNameObj) GetCode(code string) string { + return n.Get() + n.ToTitleCase(code) +} + +func (n *GeneratorNameObj) GetParamCode(param string, code string) string { + return n.GetParam(param) + n.ToTitleCase(code) +} + +func (n *GeneratorNameObj) GetObjCode(code string) string { + return n.GetObj() + n.ToTitleCase(code) +} + +func (n *GeneratorNameObj) GetMapCode(code string) string { + return n.GetMap() + n.ToTitleCase(code) +} + +func (n *GeneratorNameObj) GetTypeCode(code string) string { + return n.GetType() + n.ToTitleCase(code) +} + +func (n *GeneratorNameObj) GetTextCode(code string) string { + return n.GetText() + n.ToTitleCase(code) +} + +// + +func (n *GeneratorNameObj) Self() *GeneratorObj { + n.gen.Print(n.Get()) + return n.gen +} + +func (n *GeneratorNameObj) Param(param string) *GeneratorObj { + n.gen.Print(n.GetParam(param)) + return n.gen +} + +func (n *GeneratorNameObj) Obj() *GeneratorObj { + n.gen.Print(n.GetObj()) + return n.gen +} + +func (n *GeneratorNameObj) Map() *GeneratorObj { + n.gen.Print(n.GetMap()) + return n.gen +} + +func (n *GeneratorNameObj) Type() *GeneratorObj { + n.gen.Print(n.GetType()) + return n.gen +} + +func (n *GeneratorNameObj) Text() *GeneratorObj { + n.gen.Print(n.GetText()) + return n.gen +} + +// + +func (n *GeneratorNameObj) CodeToTitleCase(code string) *GeneratorObj { + n.gen.Print(n.ToTitleCase(code)) + return n.gen +} + +func (n *GeneratorNameObj) SelfCode(code string) *GeneratorObj { + n.gen.Print(n.GetCode(code)) + return n.gen +} + +func (n *GeneratorNameObj) SelfParam(param string, code string) *GeneratorObj { + n.gen.Print(n.GetParamCode(param, code)) + return n.gen +} + +func (n *GeneratorNameObj) SelfParamCode(param string, code string) *GeneratorObj { + n.gen.Print(n.ToTitleCase(param) + n.ToTitleCase(code)) + return n.gen +} + +func (n *GeneratorNameObj) ObjCode(code string) *GeneratorObj { + n.gen.Print(n.GetObjCode(code)) + return n.gen +} + +func (n *GeneratorNameObj) MapCode(code string) *GeneratorObj { + n.gen.Print(n.GetMapCode(code)) + return n.gen +} + +func (n *GeneratorNameObj) TypeCode(code string) *GeneratorObj { + n.gen.Print(n.GetTypeCode(code)) + return n.gen +} + +func (n *GeneratorNameObj) TextCode(code string) *GeneratorObj { + n.gen.Print(n.GetTextCode(code)) + return n.gen +} diff --git a/_run/fileGenerator/generator/saves.go b/_run/fileGenerator/generator/saves.go new file mode 100644 index 0000000..150bda4 --- /dev/null +++ b/_run/fileGenerator/generator/saves.go @@ -0,0 +1,74 @@ +package generator + +import "os" + +//###########################################################// + +type GeneratorSaveObj struct { + gen *GeneratorObj + Add GeneratorSaveAddObj + + pack string + imports []string + types []string +} + +func (gen *GeneratorObj) Save(pack string) *GeneratorSaveObj { + save := GeneratorSaveObj{} + save.Add.save = &save + + save.gen = gen + save.pack = pack + + return &save +} + +func (save *GeneratorSaveObj) End() error { + file, err := os.OpenFile(save.gen.filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer file.Close() + + file.WriteString("package " + save.pack + "\n\n") + + switch { + case len(save.imports) == 1: + file.WriteString("import \"" + save.imports[0] + "\";\n\n") + + case len(save.imports) > 1: + file.WriteString("import (\n") + for _, i := range save.imports { + file.WriteString("\t\"" + i + "\"\n") + } + file.WriteString(")\n\n") + } + + file.WriteString(hText + "\n\n") + + if len(save.types) > 0 { + for _, i := range save.types { + file.WriteString("type " + i + "\n") + } + file.WriteString("\n") + } + + file.Write(save.gen.buf.Bytes()) + return nil +} + +// + +type GeneratorSaveAddObj struct { + save *GeneratorSaveObj +} + +func (add *GeneratorSaveAddObj) Type(name string, types string) *GeneratorSaveObj { + add.save.types = append(add.save.types, name+" "+types) + return add.save +} + +func (add *GeneratorSaveAddObj) Import(imports string) *GeneratorSaveObj { + add.save.imports = append(add.save.imports, imports) + return add.save +} diff --git a/_run/fileGenerator/generator/struct.go b/_run/fileGenerator/generator/struct.go new file mode 100644 index 0000000..8979294 --- /dev/null +++ b/_run/fileGenerator/generator/struct.go @@ -0,0 +1,28 @@ +package generator + +import "bytes" + +//###########################################################// + +type GeneratorNameObj struct { + gen *GeneratorObj +} + +type GeneratorObj struct { + name string + filename string + buf bytes.Buffer + + Name GeneratorNameObj +} + +func Init(name string, file string) *GeneratorObj { + obj := GeneratorObj{} + + obj.name = name + obj.filename = file + + obj.Name.gen = &obj + + return &obj +} diff --git a/_run/fileGenerator/generator/values.go b/_run/fileGenerator/generator/values.go new file mode 100644 index 0000000..a0c8133 --- /dev/null +++ b/_run/fileGenerator/generator/values.go @@ -0,0 +1,93 @@ +package generator + +//###########################################################// + +type GeneratorValueObj struct { + maps map[any]string + delim map[any]bool + list []any + lastKey any + + Get GeneratorValueGetObj + + name *string +} + +func generatorValueInit(name *string) *GeneratorValueObj { + obj := GeneratorValueObj{} + + obj.maps = make(map[any]string) + obj.delim = make(map[any]bool) + + obj.name = name + obj.Get.arr = &obj + + return &obj +} + +func (arr *GeneratorValueObj) Add(code any, text string) *GeneratorValueObj { + arr.lastKey = code + + arr.list = append(arr.list, code) + arr.maps[code] = text + + return arr +} + +func (arr *GeneratorValueObj) AddByte(code byte, text string) *GeneratorValueObj { + arr.Add(code, text) + return arr +} + +func (arr *GeneratorValueObj) Delim() { + arr.delim[arr.lastKey] = true +} + +//// + +type GeneratorValueGetObj struct { + arr *GeneratorValueObj +} + +func (get *GeneratorValueGetObj) Text(code any) string { + return get.arr.maps[code] +} + +func (get *GeneratorValueGetObj) IsDelim(code any) bool { + _, ok := get.arr.delim[code] + return ok +} + +// + +func (get *GeneratorValueGetObj) Bytes() []byte { + bufArr := make([]byte, len(get.arr.list)) + for pos, val := range get.arr.list { + bufArr[pos] = val.(uint8) + } + return bufArr +} + +func (get *GeneratorValueGetObj) Ints() []int { + bufArr := make([]int, len(get.arr.list)) + for pos, val := range get.arr.list { + bufArr[pos] = val.(int) + } + return bufArr +} + +func (get *GeneratorValueGetObj) Uints() []uint { + bufArr := make([]uint, len(get.arr.list)) + for pos, val := range get.arr.list { + bufArr[pos] = val.(uint) + } + return bufArr +} + +func (get *GeneratorValueGetObj) Strings() []string { + bufArr := make([]string, len(get.arr.list)) + for pos, val := range get.arr.list { + bufArr[pos] = val.(string) + } + return bufArr +} diff --git a/_run/fileGenerator/main.go b/_run/fileGenerator/main.go new file mode 100644 index 0000000..aba49dc --- /dev/null +++ b/_run/fileGenerator/main.go @@ -0,0 +1,123 @@ +package main + +import ( + generator2 "github.com/hard-soft-ware/mpost/_run/fileGenerator/generator" +) + +//////////////////////////////////// + +func enumGenerator(obj *generator2.GeneratorObj, val []string) { + obj.Print("type ").Name.Type().PrintLN(" byte").LN() + + obj.PrintLN("const (") + for pos, text := range val { + obj.Offset(1).Name.SelfCode(text).Print(" ") + obj.Name.Type().Print(" = ").Number(pos).LN() + } + + obj.PrintLN(")").LN() + + // + + obj.PrintLN("const (") + + for _, text := range val { + obj.Offset(1).Name.TextCode(text).Print(" = ") + obj.String(text).LN() + } + + obj.PrintLN(")").LN() + + // + + obj.Print("var ").Name.Map().Print(" = map[").Name.Type().PrintLN("]string{") + + for _, text := range val { + obj.Offset(1).Name.SelfCode(text).Print(": ") + obj.Name.TextCode(text).PrintLN(",") + } + + obj.PrintLN("}").LN() + + // + + obj.Print("func (obj ").Name.Type().PrintLN(") String() string {") + obj.Offset(1).Print("val, ok := ").Name.Map().PrintLN("[obj]") + obj.Offset(1).PrintLN("if ok {").Offset(2).PrintLN("return val").Offset(1).PrintLN("}") + obj.Offset(1).Print("return \"Unknown ").Name.Type().PrintLN("\"") + obj.PrintLN("}") + +} + +func constGenerator(obj *generator2.GeneratorObj, val *generator2.GeneratorValueObj) { + obj.Print("type ").Name.Type().PrintLN(" byte").LN() + + obj.PrintLN("const (") + for _, code := range val.Get.Ints() { + text := val.Get.Text(code) + + obj.Offset(1).Name.SelfCode(text).Print(" ") + obj.Name.Type().Print(" = ").Hex(code).LN() + + if val.Get.IsDelim(code) { + obj.LN() + } + } + obj.PrintLN(")").LN() + + // + + obj.PrintLN("const (") + for _, code := range val.Get.Ints() { + text := val.Get.Text(code) + + obj.Offset(1).Name.TextCode(text).Print(" = ") + obj.String(text).LN() + + if val.Get.IsDelim(code) { + obj.LN() + } + } + obj.PrintLN(")").LN() + + // + + obj.Print("var ").Name.Map().Print(" = map[").Name.Type().PrintLN("]string{") + for _, code := range val.Get.Ints() { + text := val.Get.Text(code) + + obj.Offset(1).Name.SelfCode(text).Print(": ") + obj.Name.TextCode(text).PrintLN(",") + + } + obj.PrintLN("}").LN() + + obj.Print("func (obj ").Name.Type().PrintLN(") String() string {") + obj.Offset(1).Print("val, ok := ").Name.Map().PrintLN("[obj]") + obj.Offset(1).PrintLN("if ok {").Offset(2).PrintLN("return val").Offset(1).PrintLN("}") + obj.Offset(1).Print("return \"Unknown ").Name.Type().PrintLN("\"") + obj.PrintLN("}").LN() + + obj.Print("func (obj ").Name.Type().PrintLN(") Byte() byte {") + obj.Offset(1).PrintLN("return byte(obj)") + obj.PrintLN("}") + +} + +//////// + +func main() { + Status() + Document() + Orientation() + OrientationControl() + PowerUp() + PupExt() + State() + Bezel() + Event() + + Cmd() + CmdAux() + Data() +} diff --git a/_run/scripts/creator_const_Go.sh b/_run/scripts/creator_const_Go.sh index 5a25449..a7ec672 100644 --- a/_run/scripts/creator_const_Go.sh +++ b/_run/scripts/creator_const_Go.sh @@ -2,7 +2,7 @@ # Original source: https://github.com/Bookshelf-Writer/scripts-for-integration/blob/main/_run/scripts/creator_const_Go.sh dir_path="" -file_name="verControl.go" +file_name="const.go" package_name="mpost" ############################################################################# diff --git a/_run/values/ver.txt b/_run/values/ver.txt index 04e1946..00a2c6f 100644 --- a/_run/values/ver.txt +++ b/_run/values/ver.txt @@ -1 +1 @@ -v0.1.3 +v0.1.16 diff --git a/acceptor.go b/acceptor.go index 7e47557..0eaa490 100644 --- a/acceptor.go +++ b/acceptor.go @@ -1,176 +1,59 @@ package mpost import ( + "context" + "github.com/hard-soft-ware/mpost/enum" + "github.com/hard-soft-ware/mpost/serial" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "go.bug.st/serial" - "os" - "sync" - "time" ) //////////////////////////////////// +type EventHandler func(*CAcceptor, int) + type CAcceptor struct { - port serial.Port + port *serial.SerialStruct auditLifeTimeTotals []int auditPerformance []int auditQP []int - autoStack bool - barCode string - - bill CBill - billTypes []CBill - billTypeEnables []bool - billValues []CBill - billValueEnables []bool - - bnfStatus BNFStatus - bootPN string - capApplicationID bool - capApplicationPN bool - capAssetNumber bool - capAudit bool - capBarCodes bool - capBarCodesExt bool - capBNFStatus bool - capBookmark bool - capBootPN bool - capCalibrate bool - capCashBoxTotal bool - capCouponExt bool - capDevicePaused bool - capDeviceSoftReset bool - capDeviceType bool - capDeviceResets bool - capDeviceSerialNumber bool - capEscrowTimeout bool - capFlashDownload bool - capNoPush bool - capOrientationExt bool - capPupExt bool - capTestDoc bool - capVariantID bool - capVariantPN bool - cashBoxAttached bool - cashBoxFull bool - cashBoxTotal int - connected bool - coupon *CCoupon - deviceFailure bool - deviceModel int - devicePaused bool - devicePortName string - devicePowerUp PowerUp - deviceResets int - deviceRevision int - deviceSerialNumber string - deviceStalled bool - deviceState State - deviceType string - docType DocumentType - enableAcceptance bool - enableBarCodes bool - enableBookmarks bool - enableCouponExt bool - enableNoPush bool - escrowOrientation Orientation - highSecurity bool - orientationCtl OrientationControl - orientationCtlExt OrientationControl - version string - transactionTimeout time.Duration - downloadTimeout time.Duration - inSoftResetOneSecondIgnore bool - inSoftResetWaitForReply bool - expandedNoteReporting bool - isQueryDeviceCapabilitiesSupported bool - isDeviceJammed bool - isCheated bool - isPoweredUp bool - isInvalidCommand bool - wasDocTypeSetOnEscrow bool - wasDisconnected bool - isVeryFirstPoll bool - shouldRaiseConnectedEvent bool - shouldRaiseEscrowEvent bool - shouldRaisePUPEscrowEvent bool - shouldRaiseStackedEvent bool - shouldRaiseReturnedEvent bool - shouldRaiseRejectedEvent bool - shouldRaiseCheatedEvent bool - shouldRaiseStackerFullEvent bool - shouldRaiseCalibrateStartEvent bool - shouldRaiseCalibrateProgressEvent bool - shouldRaiseCalibrateFinishEvent bool - shouldRaiseDownloadStartEvent bool - shouldRaiseDownloadRestartEvent bool - shouldRaiseDownloadProgressEvent bool - shouldRaiseDownloadFinishEvent bool - shouldRaisePauseDetectedEvent bool - shouldRaisePauseClearedEvent bool - shouldRaiseStallDetectedEvent bool - shouldRaiseStallClearedEvent bool - shouldRaiseJamDetectedEvent bool - shouldRaiseJamClearedEvent bool - shouldRaisePowerUpEvent bool - shouldRaiseInvalidCommandEvent bool - shouldRaiseCashBoxAttachedEvent bool - shouldRaiseCashBoxRemovedEvent bool - shouldRaiseDisconnectedEvent bool - compressLog bool - workerThread sync.WaitGroup - openThread chan bool - flashDownloadThread chan bool - dataLinkLayer *CDataLinkLayer - replyQueuedEvent int - notInProcessReplyEvent int - stopWorkerThread bool - stopOpenThread bool - stopFlashDownloadThread bool - suppressStandardPoll bool + + coupon *CCoupon + + docType enum.DocumentType + + openThread bool + flashDownloadThread bool + dataLinkLayer *CDataLinkLayer messageQueue chan *CMessage replyQueue chan []byte - wasStopped bool - isReplyAcked bool - signalMainThreadEvent int - eventHandlers map[Event]EventHandler + eventHandlers map[enum.EventType]EventHandler + + log LogStruct + + Ctx context.Context + CtxCancel context.CancelFunc + ss bool +} + +var DefAcceptor = &CAcceptor{ + eventHandlers: make(map[enum.EventType]EventHandler, enum.Event_End), - log *LogGlobalStruct + messageQueue: make(chan *CMessage, 1), + replyQueue: make(chan []byte, 1), } -func NewCAcceptor(transactionTimeout, downloadTimeout time.Duration) *CAcceptor { - a := &CAcceptor{ - transactionTimeout: transactionTimeout, - downloadTimeout: downloadTimeout, - eventHandlers: make(map[Event]EventHandler, Events_End), - - messageQueue: make(chan *CMessage, 1), - replyQueue: make(chan []byte, 1), - flashDownloadThread: make(chan bool, 1), - openThread: make(chan bool, 1), - - log: NewLog( - log.Output(zerolog.ConsoleWriter{ - Out: os.Stdout, - NoColor: false, - TimeFormat: "15:04:05", - }), - "Acceptor", - ), - } - - return a +func init() { + DefAcceptor.Ctx, DefAcceptor.CtxCancel = context.WithCancel(context.Background()) } // -func (a *CAcceptor) getTickCount() int64 { - return time.Now().UnixNano() / int64(time.Millisecond) +func (a *CAcceptor) AddLog(log zerolog.Logger, root string, printBytes bool) { + a.log = NewLog(log, root, printBytes) } -func (a *CAcceptor) SetEventHandler(event Event, eventHandler func(*CAcceptor, int)) { - a.eventHandlers[event] = eventHandler +func (a *CAcceptor) AddHook(ev enum.EventType, h EventHandler) { + a.eventHandlers[ev] = h } diff --git a/acceptor/acceptor.go b/acceptor/acceptor.go new file mode 100644 index 0000000..3775cee --- /dev/null +++ b/acceptor/acceptor.go @@ -0,0 +1,109 @@ +package acceptor + +import ( + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" +) + +//////////////////////////////////// + +var Connected bool +var AutoStack bool + +var EscrowOrientation enum.OrientationType +var HighSecurity bool +var OrientationCtl enum.OrientationControlType +var OrientationCtlExt enum.OrientationControlType + +var Version string +var BarCode string +var BootPN string + +var InSoftResetOneSecondIgnore bool +var InSoftResetWaitForReply bool +var ExpandedNoteReporting bool +var IsQueryDeviceCapabilitiesSupported bool +var IsCheated bool +var IsPoweredUp bool +var IsInvalidCommand bool +var WasDocTypeSetOnEscrow bool +var WasDisconnected bool +var IsVeryFirstPoll bool + +var StopWorkerThread bool +var StopOpenThread bool +var StopFlashDownloadThread bool +var SuppressStandardPoll bool +var WasStopped bool + +//// + +func ConstructOmnibusCommand(payload []byte, controlCode consts.CmdType, data0Index int, billTypeEnables []bool) { + payload[0] = controlCode.Byte() + + if Enable.Bookmarks && Enable.Acceptance && Device.State != enum.StateCalibrating { + payload[0] |= 0x20 + } + + data0 := byte(0) + + if Enable.Acceptance && Device.State != enum.StateCalibrating { + if ExpandedNoteReporting { + data0 |= 0x7F + } else { + if len(billTypeEnables) == 0 { + data0 |= 0x7F + } else { + for i, enable := range billTypeEnables { + if enable { + data0 |= 1 << i + } + } + } + } + } + + data1 := byte(0) // Ignore bit 0 since we are not supporting special interrupt mode. + + if HighSecurity { + data1 |= 0x02 + } + + switch OrientationCtl { + case enum.OrientationControlTwoWay: + data1 |= 0x04 + case enum.OrientationControlFourWay: + data1 |= 0x0C + } + + data1 |= 0x10 // Always enable escrow mode. + + data2 := byte(0) + + if Enable.NoPush { + data2 |= 0x01 + } + + if Enable.BarCodes && Enable.Acceptance && Device.State != enum.StateCalibrating { + data2 |= 0x02 + } + + switch Device.PowerUp { + case enum.PowerUpB: + data2 |= 0x04 + case enum.PowerUpC: + data2 |= 0x0C + } + + if ExpandedNoteReporting { + data2 |= 0x10 + } + + if Enable.CouponExt && Cap.CouponExt { + data2 |= 0x20 + } + + payload[data0Index] = data0 + payload[data0Index+1] = data1 + payload[data0Index+2] = data2 +} diff --git a/acceptor/cap.go b/acceptor/cap.go new file mode 100644 index 0000000..8c9a60e --- /dev/null +++ b/acceptor/cap.go @@ -0,0 +1,35 @@ +package acceptor + +//////////////////////////////////// + +type CapStruct struct { + ApplicationID bool + ApplicationPN bool + AssetNumber bool + Audit bool + BarCodes bool + BarCodesExt bool + BNFStatus bool + Bookmark bool + BootPN bool + Calibrate bool + CashBoxTotal bool + CouponExt bool + DevicePaused bool + DeviceSoftReset bool + DeviceType bool + DeviceResets bool + DeviceSerialNumber bool + EscrowTimeout bool + FlashDownload bool + NoPush bool + OrientationExt bool + PupExt bool + TestDoc bool + VariantID bool + VariantPN bool +} + +var Cap CapStruct + +//// diff --git a/acceptor/cash.go b/acceptor/cash.go new file mode 100644 index 0000000..0f07f27 --- /dev/null +++ b/acceptor/cash.go @@ -0,0 +1,13 @@ +package acceptor + +//////////////////////////////////// + +type CashStruct struct { + BoxAttached bool + BoxFull bool + BoxTotal int +} + +var Cash CashStruct + +//// diff --git a/acceptor/device.go b/acceptor/device.go new file mode 100644 index 0000000..20f5944 --- /dev/null +++ b/acceptor/device.go @@ -0,0 +1,21 @@ +package acceptor + +import "github.com/hard-soft-ware/mpost/enum" + +//////////////////////////////////// + +type DeviceStruct struct { + Failure bool + Model int + Paused bool + PowerUp enum.PowerUpType + Resets int + Revision int + Stalled bool + State enum.StateType + Type string + + Jammed bool +} + +var Device DeviceStruct diff --git a/acceptor/enable.go b/acceptor/enable.go new file mode 100644 index 0000000..2ee6d59 --- /dev/null +++ b/acceptor/enable.go @@ -0,0 +1,13 @@ +package acceptor + +//////////////////////////////////// + +type EnableStruct struct { + Acceptance bool + BarCodes bool + Bookmarks bool + CouponExt bool + NoPush bool +} + +var Enable EnableStruct diff --git a/acceptor/shouldRaise.go b/acceptor/shouldRaise.go new file mode 100644 index 0000000..86bcdc3 --- /dev/null +++ b/acceptor/shouldRaise.go @@ -0,0 +1,34 @@ +package acceptor + +//////////////////////////////////// + +type ShouldRaiseStruct struct { + ConnectedEvent bool + EscrowEvent bool + PUPEscrowEvent bool + StackedEvent bool + ReturnedEvent bool + RejectedEvent bool + CheatedEvent bool + StackerFullEvent bool + CalibrateStartEvent bool + CalibrateProgressEvent bool + CalibrateFinishEvent bool + DownloadStartEvent bool + DownloadRestartEvent bool + DownloadProgressEvent bool + DownloadFinishEvent bool + PauseDetectedEvent bool + PauseClearedEvent bool + StallDetectedEvent bool + StallClearedEvent bool + JamDetectedEvent bool + JamClearedEvent bool + PowerUpEvent bool + InvalidCommandEvent bool + CashBoxAttachedEvent bool + CashBoxRemovedEvent bool + DisconnectedEvent bool +} + +var ShouldRaise ShouldRaiseStruct diff --git a/acceptor/timeout.go b/acceptor/timeout.go new file mode 100644 index 0000000..c4f00e4 --- /dev/null +++ b/acceptor/timeout.go @@ -0,0 +1,12 @@ +package acceptor + +import "time" + +//////////////////////////////////// + +type TimeoutStruct struct { + Transaction time.Duration + Download time.Duration +} + +var Timeout TimeoutStruct diff --git a/acceptor_command.go b/acceptor_command.go deleted file mode 100644 index 8320d25..0000000 --- a/acceptor_command.go +++ /dev/null @@ -1,93 +0,0 @@ -package mpost - -import ( - "errors" - "time" -) - -//////////////////////////////////// - -func (a *CAcceptor) ConstructOmnibusCommand(payload []byte, controlCode byte, data0Index int) { - payload[0] = controlCode - - if a.enableBookmarks && a.enableAcceptance && a.deviceState != Calibrating { - payload[0] |= 0x20 - } - - data0 := byte(0) - - if a.enableAcceptance && a.deviceState != Calibrating { - if a.expandedNoteReporting { - data0 |= 0x7F - } else { - if len(a.billTypeEnables) == 0 { - data0 |= 0x7F - } else { - for i, enable := range a.billTypeEnables { - if enable { - data0 |= 1 << i - } - } - } - } - } - - data1 := byte(0) // Ignore bit 0 since we are not supporting special interrupt mode. - - if a.highSecurity { - data1 |= 0x02 - } - - switch a.orientationCtl { - case TwoWay: - data1 |= 0x04 - case FourWay: - data1 |= 0x0C - } - - data1 |= 0x10 // Always enable escrow mode. - - data2 := byte(0) - - if a.enableNoPush { - data2 |= 0x01 - } - - if a.enableBarCodes && a.enableAcceptance && a.deviceState != Calibrating { - data2 |= 0x02 - } - - switch a.devicePowerUp { - case B: - data2 |= 0x04 - case C: - data2 |= 0x0C - } - - if a.expandedNoteReporting { - data2 |= 0x10 - } - - if a.enableCouponExt && a.capCouponExt { - data2 |= 0x20 - } - - payload[data0Index] = data0 - payload[data0Index+1] = data1 - payload[data0Index+2] = data2 -} - -//// - -func (a *CAcceptor) SendSynchronousCommand(payload []byte) ([]byte, error) { - a.log.Debug().Bytes("payload", payload).Msg("SendCommand") - a.messageQueue <- NewCMessage(payload, true) - - select { - case reply := <-a.replyQueue: - a.log.Debug().Bytes("payload", reply).Msg("Reply queued") - return reply, nil - case <-time.After(30 * time.Second): - return nil, errors.New("timeout waiting for response") - } -} diff --git a/acceptor_flash.go b/acceptor_flash.go new file mode 100644 index 0000000..9d0f425 --- /dev/null +++ b/acceptor_flash.go @@ -0,0 +1,155 @@ +package mpost + +import ( + "errors" + "fmt" + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" + "os" + "time" +) + +//////////////////////////////////// + +func (a *CAcceptor) FlashDownload(filePath string) (err error) { + a.log.Msg("FlashDownload") + + if !acceptor.Connected && acceptor.Device.State != enum.StateDownloadRestart { + err = errors.New("FlashDownload not allowed when not connected or not in DownloadRestart state") + a.log.Err("FlashDownload", err) + return + } + + if !(acceptor.Device.Model >= 65 && acceptor.Device.Model <= 71 || + acceptor.Device.Model == 77 || + acceptor.Device.Model == 80 || + acceptor.Device.Model == 81 || + acceptor.Device.Model == 87 || + acceptor.Device.Model == 88) { + if acceptor.Device.State != enum.StateIdling && acceptor.Device.State != enum.StateDownloadRestart { + err = errors.New("FlashDownload allowed only when DeviceState is Idling or DownloadRestart") + a.log.Err("FlashDownload", err) + return + } + } + + file, err := os.Open(filePath) + if err != nil { + err = errors.New(fmt.Sprintf("Failed to open flash download file: %s", err)) + a.log.Err("FlashDownload", err) + return + } + defer file.Close() + + // Determine the file size and check if divisible by 32 + fileInfo, err := file.Stat() + if err != nil { + err = errors.New(fmt.Sprintf("Failed to stat flash download file: %s", err)) + a.log.Err("FlashDownload", err) + return + } + fileSize := fileInfo.Size() + if fileSize%32 != 0 { + err = errors.New("Flash download file size must be divisible by 32") + a.log.Err("FlashDownload", err) + return + } + + go func() { + defer file.Close() + a.flashDownload(file, fileSize) + }() + + return +} + +func (a *CAcceptor) flashDownload(downloadFile *os.File, fileSize int64) { + a.log.Msg("FlashDownloadThread started") + + if acceptor.Device.State != enum.StateDownloadRestart { + acceptor.Device.State = enum.StateDownloadStart + } + + packetNum := 0 + finalPacketNum := int(fileSize / 32) + payload := []byte{consts.CmdCalibrate.Byte(), 0x00, 0x00, 0x00} + var reply []byte + var err error + + for { + if acceptor.StopFlashDownloadThread { + acceptor.Device.State = enum.StateIdling + return + } + + reply, err = a.SendSynchronousCommand(payload) + if err != nil || len(reply) == 0 { + if !acceptor.Connected { + a.RaiseDownloadFinishEvent(false) + acceptor.Device.State = enum.StateIdling + return + } + continue + } + + if (reply[2] & 0x70) == 0x20 { + break + } + packetNum = (int(reply[3]&0x0F)<<12 + int(reply[4]&0x0F)<<8 + int(reply[5]&0x0F)<<4 + int(reply[6]&0x0F) + 1) & 0xFFFF + } + + a.RaiseDownloadStartEvent(finalPacketNum) + timeoutStartTickCount := time.Now() + + for packetNum < finalPacketNum { + if acceptor.StopFlashDownloadThread { + acceptor.Device.State = enum.StateIdling + return + } + + dataBuffer := make([]byte, 32) + _, err := downloadFile.ReadAt(dataBuffer, int64(packetNum*32)) + if err != nil { + time.Sleep(200 * time.Millisecond) + continue + } + + payload = make([]byte, 69) + payload[0] = consts.CmdFlashDownload.Byte() + payload[1] = byte((packetNum & 0xF000) >> 12) + payload[2] = byte((packetNum & 0x0F00) >> 8) + payload[3] = byte((packetNum & 0x00F0) >> 4) + payload[4] = byte(packetNum & 0x000F) + copy(payload[5:], dataBuffer) + + reply, err = a.SendSynchronousCommand(payload) + if err != nil || len(reply) != 9 { + time.Sleep(200 * time.Millisecond) + continue + } + + if reply[0] == consts.DataSTX.Byte() { + acceptor.Device.State = enum.StateDownloading + if acceptor.ShouldRaise.DownloadProgressEvent { + a.RaiseDownloadProgressEvent(packetNum) + } + packetNum++ + } else { + time.Sleep(200 * time.Millisecond) + continue + } + + if time.Since(timeoutStartTickCount) > acceptor.Timeout.Download { + a.RaiseDownloadFinishEvent(false) + acceptor.Device.State = enum.StateIdling + return + } + } + + time.Sleep(30 * time.Millisecond) + a.RaiseDownloadFinishEvent(true) + acceptor.Device.State = enum.StateIdling + acceptor.Connected = true + a.RaiseConnectedEvent() +} diff --git a/acceptor_loop.go b/acceptor_loop.go index d35a477..57aa7bb 100644 --- a/acceptor_loop.go +++ b/acceptor_loop.go @@ -1,43 +1,45 @@ package mpost -import "time" +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" + "time" +) //////////////////////////////////// -func (a *CAcceptor) PollingLoop(lg *LogGlobalStruct) []byte { +func (a *CAcceptor) PollingLoop() []byte { startTickCount := time.Now() for { - payload := []byte{CmdOmnibus, 0x00, 0x10, 0x00} + payload := []byte{consts.CmdOmnibus.Byte(), 0x00, 0x10, 0x00} reply, err := a.SendSynchronousCommand(payload) if err != nil { - lg.Debug().Err(err).Msg("PollingLoop") + a.log.Err("PollingLoop", err) } if time.Since(startTickCount) > PollingDisconnectTimeout { - if a.shouldRaiseDisconnectedEvent { - a.RaiseDisconnectedEvent() - } + a.Close() startTickCount = time.Now() } - if a.flashDownloadThread != nil { - if a.stopFlashDownloadThread { - a.stopFlashDownloadThread = true - <-a.flashDownloadThread - a.deviceState = Idling - a.wasStopped = true + if !a.flashDownloadThread { + if acceptor.StopFlashDownloadThread { + acceptor.StopFlashDownloadThread = true + a.flashDownloadThread = true + acceptor.Device.State = enum.StateIdling + acceptor.WasStopped = true return nil } - } else if a.openThread != nil { - if a.stopOpenThread { - a.stopOpenThread = false - a.stopWorkerThread = true - - <-a.openThread - - a.wasStopped = true + } + if !a.openThread { + if acceptor.StopOpenThread { + acceptor.StopOpenThread = false + acceptor.StopWorkerThread = true + a.openThread = true + acceptor.WasStopped = true a.Close() return nil } diff --git a/acceptor_message.go b/acceptor_message.go index d9ca105..1b92172 100644 --- a/acceptor_message.go +++ b/acceptor_message.go @@ -1,5 +1,10 @@ package mpost +import ( + "errors" + "time" +) + //////////////////////////////////// type CMessage struct { @@ -15,3 +20,39 @@ func NewCMessage(payload []byte, isSynchronous bool) *CMessage { IsSynchronous: isSynchronous, } } + +//// + +func (a *CAcceptor) SendSynchronousCommand(payload []byte) ([]byte, error) { + if !a.ss { + a.ss = true + } else { + a.ss = false + payload[0] += 1 + } + + a.messageQueue <- NewCMessage(payload, true) + + select { + case <-a.Ctx.Done(): + a.Close() + return nil, errors.New("close from context") + case reply := <-a.replyQueue: + return reply, nil + case <-time.After(30 * time.Second): + return nil, errors.New("timeout waiting for response") + } + + return nil, errors.New("invalid response") +} + +func (a *CAcceptor) SendAsynchronousCommand(payload []byte) { + if !a.ss { + a.ss = true + } else { + a.ss = false + payload[0] += 1 + } + + a.messageQueue <- NewCMessage(payload, false) +} diff --git a/acceptor_methods.go b/acceptor_methods.go index 930cff7..31ef919 100644 --- a/acceptor_methods.go +++ b/acceptor_methods.go @@ -1,10 +1,18 @@ package mpost -import "fmt" +import ( + "errors" + "fmt" + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/bill" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" + "time" +) // ////////////////////////////////// func (a *CAcceptor) verifyPropertyIsAllowed(capabilityFlag bool, propertyName string) error { - if !a.connected { + if !acceptor.Connected { return fmt.Errorf("Calling %s not allowed when not connected.", propertyName) } @@ -12,27 +20,631 @@ func (a *CAcceptor) verifyPropertyIsAllowed(capabilityFlag bool, propertyName st return fmt.Errorf("Device does not support %s.", propertyName) } - switch a.deviceState { - case DownloadStart, Downloading: + switch acceptor.Device.State { + case enum.StateDownloadStart, enum.StateDownloading: return fmt.Errorf("Calling %s not allowed during flash download.", propertyName) - case CalibrateStart, Calibrating: + case enum.StateCalibrateStart, enum.StateCalibrating: return fmt.Errorf("Calling %s not allowed during calibration.", propertyName) } return nil } +//// + +func (a *CAcceptor) GetApplicationID() string { + a.log.Msg("GetApplicationID") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.ApplicationID, "GetApplicationID") + if err != nil { + a.log.Err("GetApplicationID", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorApplicationID.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetApplicationID", err) + return "" + } + + if len(reply) == 14 { + s := string(reply[3:12]) + return s + } + + return "" +} + +func (a *CAcceptor) GetApplicationPN() string { + a.log.Msg("GetApplicationPN") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.ApplicationPN, "ApplicationPN") + if err != nil { + a.log.Err("ApplicationPN", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorApplicationPartNumber.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetApplicationPN", err) + return "" + } + + if len(reply) == 14 { + s := string(reply[3:12]) + return s + } + + return "" +} + +func (a *CAcceptor) GetAuditLifeTimeTotals() []int { + a.log.Msg("GetAuditLifeTimeTotals") + values := []int{} + + err := a.verifyPropertyIsAllowed(acceptor.Cap.Audit, "GetAuditLifeTimeTotals") + if err != nil { + a.log.Err("GetAuditLifeTimeTotals", err) + return values + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorAuditLifeTimeTotals.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetAuditLifeTimeTotals", err) + return values + } + + if len(reply) < 13 || ((len(reply)-5)%8 != 0) { + return values + } + + fieldCount := (len(reply) - 5) / 8 + for i := 0; i < fieldCount; i++ { + offset := 8*i + 3 + value := (int((reply)[offset+0]&0x0F) << 28) + + (int((reply)[offset+1]&0x0F) << 24) + + (int((reply)[offset+2]&0x0F) << 20) + + (int((reply)[offset+3]&0x0F) << 16) + + (int((reply)[offset+4]&0x0F) << 12) + + (int((reply)[offset+5]&0x0F) << 8) + + (int((reply)[offset+6]&0x0F) << 4) + + int((reply)[offset+7]&0x0F) + + values = append(values, value) + } + + return values +} + +func (a *CAcceptor) GetAuditPerformance() []int { + a.log.Msg("GetAuditPerformance") + values := []int{} + + err := a.verifyPropertyIsAllowed(acceptor.Cap.Audit, "GetAuditPerformance") + if err != nil { + a.log.Err("GetAuditPerformance", err) + return values + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorAuditPerformanceMeasures.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetAuditPerformance", err) + return values + } + + if len(reply) < 9 || ((len(reply)-5)%4 != 0) { + return values + } + + fieldCount := (len(reply) - 5) / 4 + + for i := 0; i < fieldCount; i++ { + offset := 4*i + 3 + value := (int((reply)[offset+0]&0x0F) << 12) + + (int((reply)[offset+1]&0x0F) << 8) + + (int((reply)[offset+2]&0x0F) << 4) + + int((reply)[offset+3]&0x0F) + + values = append(values, value) + } + + return values +} + +func (a *CAcceptor) GetAuditQP() []int { + a.log.Msg("GetAuditQP") + values := []int{} + + err := a.verifyPropertyIsAllowed(acceptor.Cap.Audit, "GetAuditQP") + if err != nil { + a.log.Err("GetAuditQP", err) + return values + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorAuditQPMeasures.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetAuditQP", err) + return values + } + + if len(reply) < 9 || ((len(reply)-5)%4 != 0) { + return values + } + + fieldCount := (len(reply) - 5) / 4 + + for i := 0; i < fieldCount; i++ { + offset := 4*i + 3 + value := (int(reply[offset+0]&0x0F) << 12) + + (int(reply[offset+1]&0x0F) << 8) + + (int(reply[offset+2]&0x0F) << 4) + + int(reply[offset+3]&0x0F) + + values = append(values, value) + } + + return values +} + +func (a *CAcceptor) GetAutoStack() bool { + a.log.Msg("GetAutoStack") + return acceptor.AutoStack +} + +func (a *CAcceptor) SetAutoStack(v bool) { + a.log.Msg("SetAutoStack") + acceptor.AutoStack = v +} + +func (a *CAcceptor) GetBarCode() string { + a.log.Msg("GetBarCode") + return acceptor.BarCode +} + +func (a *CAcceptor) GetBill() bill.BillStruct { + a.log.Msg("GetBill") + return bill.Bill +} + +func (a *CAcceptor) GetBillTypes() []bill.BillStruct { + a.log.Msg("GetBillTypes") + return bill.Types +} + +func (a *CAcceptor) GetBillTypeEnables() []bool { + a.log.Msg("GetBillTypeEnables") + return bill.TypeEnables +} + +func (a *CAcceptor) SetBillTypeEnables(v []bool) { + a.log.Msg("SetBillTypeEnables") + + if !acceptor.Connected { + a.log.Err("SetBillTypeEnables", errors.New("calling BillTypeEnables not allowed when not connected")) + return + } + + if len(bill.TypeEnables) != len(bill.Types) { + a.log.Err("SetBillTypeEnables", fmt.Errorf("CBillTypeEnables size must match BillTypes size")) + return + } + + bill.TypeEnables = v + + if acceptor.ExpandedNoteReporting { + payload := make([]byte, 15) + acceptor.ConstructOmnibusCommand(payload, consts.CmdExpanded, 2, bill.TypeEnables) + payload[1] = 0x03 // Sub Type + + for i, enable := range bill.TypeEnables { + enableIndex := i / 7 + bitPosition := i % 7 + bit := 1 << bitPosition + + if enable { + payload[5+enableIndex] |= byte(bit) + } + } + + a.SendAsynchronousCommand(payload) + } +} + +func (a *CAcceptor) GetBillValues() []bill.BillStruct { + a.log.Msg("GetBillValues") + return bill.Values +} + +func (a *CAcceptor) GetBillValueEnables() []bool { + a.log.Msg("GetBillValueEnables") + return bill.ValueEnables +} + +func (a *CAcceptor) SetBillValueEnables(v []bool) { + a.log.Msg("SetBillValueEnables") + bill.ValueEnables = v + + for _, enabled := range bill.ValueEnables { + for j, billType := range bill.Types { + if billType.Value == bill.Values[j].Value && billType.Country == bill.Values[j].Country { + bill.TypeEnables[j] = enabled + } + } + } + + payload := make([]byte, 15) + acceptor.ConstructOmnibusCommand(payload, consts.CmdExpanded, 2, bill.TypeEnables) + payload[1] = 0x03 // Sub Type + + for i, enable := range bill.TypeEnables { + enableIndex := i / 7 + bitPosition := i % 7 + bit := 1 << bitPosition + + if enable { + payload[5+enableIndex] |= byte(bit) + } + } + + a.SendAsynchronousCommand(payload) +} + +func (a *CAcceptor) GetBNFStatus() enum.BNFStatusType { + a.log.Msg("Getting BNF status") + err := a.verifyPropertyIsAllowed(acceptor.Cap.BNFStatus, "BNFStatus") + + if err != nil { + a.log.Err("GetBNFStatus", err) + return enum.BNFStatusUnknown + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxBNFStatus.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetBNFStatus", err) + return enum.BNFStatusUnknown + } + + if len(reply) == 9 { + if reply[3] == 0 { + return enum.BNFStatusNotAttached + } else { + if reply[4] == 0 { + return enum.BNFStatusOK + } else { + return enum.BNFStatusError + } + } + } + + return enum.BNFStatusUnknown +} + +func (a *CAcceptor) GetBootPN() string { + a.log.Msg("GetBootPN") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.BootPN, "GetBootPN") + if err != nil { + a.log.Err("GetBootPN", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorBootPartNumber.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetBootPN", err) + return "" + } + + if len(reply) == 14 { + s := string(reply[3:12]) // Extracting the substring from byte slice + return s + } + + return "" +} + +func (a *CAcceptor) GetCapApplicationID() bool { + a.log.Msg("GetCapApplicationID") + return acceptor.Cap.ApplicationID +} + +func (a *CAcceptor) GetCapApplicationPN() bool { + a.log.Msg("GetCapApplicationPN") + return acceptor.Cap.ApplicationPN +} + +func (a *CAcceptor) GetCapAssetNumber() bool { + a.log.Msg("GetCapAssetNumber") + return acceptor.Cap.AssetNumber +} + +func (a *CAcceptor) GetCapAudit() bool { + a.log.Msg("GetCapAudit") + return acceptor.Cap.Audit +} + +func (a *CAcceptor) GetCapBarCodes() bool { + a.log.Msg("GetCapBarCodes") + return acceptor.Cap.BarCodes +} + +func (a *CAcceptor) GetCapBarCodesExt() bool { + a.log.Msg("GetCapBarCodesExt") + return acceptor.Cap.BarCodesExt +} + +func (a *CAcceptor) GetCapBNFStatus() bool { + a.log.Msg("GetCapBNFStatus") + return acceptor.Cap.BNFStatus +} + +func (a *CAcceptor) GetCapBookmark() bool { + a.log.Msg("GetCapBookmark") + return acceptor.Cap.Bookmark +} + +func (a *CAcceptor) GetCapBootPN() bool { + a.log.Msg("GetCapBootPN") + return acceptor.Cap.BootPN +} + +func (a *CAcceptor) GetCapCalibrate() bool { + a.log.Msg("GetCapCalibrate") + return acceptor.Cap.Calibrate +} + +func (a *CAcceptor) GetCapCashBoxTotal() bool { + a.log.Msg("GetCapCashBoxTotal") + return acceptor.Cap.CashBoxTotal +} + +func (a *CAcceptor) GetCapCouponExt() bool { + a.log.Msg("GetCapCouponExt") + return acceptor.Cap.CouponExt +} + +func (a *CAcceptor) GetCapDevicePaused() bool { + a.log.Msg("GetCapDevicePaused") + return acceptor.Cap.DevicePaused +} + +func (a *CAcceptor) GetCapDeviceSoftReset() bool { + a.log.Msg("GetCapDeviceSoftReset") + return acceptor.Cap.DeviceSoftReset +} + +func (a *CAcceptor) GetCapDeviceType() bool { + a.log.Msg("GetCapDeviceType") + return acceptor.Cap.DeviceType +} + +func (a *CAcceptor) GetCapDeviceResets() bool { + a.log.Msg("GetCapDeviceResets") + return acceptor.Cap.DeviceResets +} + +func (a *CAcceptor) GetCapDeviceSerialNumber() bool { + a.log.Msg("GetCapDeviceSerialNumber") + return acceptor.Cap.DeviceSerialNumber +} + +func (a *CAcceptor) GetCapEscrowTimeout() bool { + a.log.Msg("GetCapEscrowTimeout") + return acceptor.Cap.EscrowTimeout +} + +func (a *CAcceptor) GetCapFlashDownload() bool { + a.log.Msg("GetCapFlashDownload") + return acceptor.Cap.FlashDownload +} + +func (a *CAcceptor) GetCapNoPush() bool { + a.log.Msg("GetCapNoPush") + return acceptor.Cap.NoPush +} + +func (a *CAcceptor) GetCapOrientationExt() bool { + a.log.Msg("GetCapOrientationExt") + return acceptor.Cap.OrientationExt +} + +func (a *CAcceptor) GetCapPupExt() bool { + a.log.Msg("GetCapPupExt") + return acceptor.Cap.PupExt +} + +func (a *CAcceptor) GetCapTestDoc() bool { + a.log.Msg("GetCapTestDoc") + return acceptor.Cap.TestDoc +} + +func (a *CAcceptor) GetCapVariantID() bool { + a.log.Msg("GetCapVariantID") + return acceptor.Cap.VariantID +} + +func (a *CAcceptor) GetCapVariantPN() bool { + a.log.Msg("GetCapVariantPN") + return acceptor.Cap.VariantPN +} + +func (a *CAcceptor) GetCashBoxAttached() bool { + a.log.Msg("GetCashBoxAttached") + return acceptor.Cash.BoxAttached +} + +func (a *CAcceptor) GetCashBoxFull() bool { + a.log.Msg("GetCashBoxFull") + return acceptor.Cash.BoxFull +} + +func (a *CAcceptor) GetCashBoxTotal() int64 { + a.log.Msg("GetCashBoxTotal") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.CashBoxTotal, "GetCashBoxTotal") + if err != nil { + a.log.Err("GetCashBoxTotal", err) + return 0 + } + + payload := []byte{consts.CmdOmnibus.Byte(), 0x7F, 0x3C, 0x02} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetCashBoxTotal", err) + return 0 + } + + if len(reply) < 9 { + return 0 + } + + total := int64(reply[3]&0x0F)<<20 | + int64(reply[4]&0x0F)<<16 | + int64(reply[5]&0x0F)<<12 | + int64(reply[6]&0x0F)<<8 | + int64(reply[7]&0x0F)<<4 | + int64(reply[8]&0x0F) + + return total +} + +func (a *CAcceptor) GetConnected() bool { + a.log.Msg("GetConnected") + return acceptor.Connected +} + +func (a *CAcceptor) GetCoupon() *CCoupon { + a.log.Msg("GetCoupon") + return a.coupon +} + +func (a *CAcceptor) GetDeviceBusy() bool { + a.log.Msg("GetDeviceBusy") + return acceptor.Device.State != enum.StateIdling +} + +func (a *CAcceptor) GetDeviceCRC() int64 { + a.log.Msg("GetDeviceCRC") + + err := a.verifyPropertyIsAllowed(true, "DeviceCRC") + if err != nil { + a.log.Err("GetDeviceCRC", err) + return 0 + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxSoftwareCRC.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetDeviceCRC", err) + return 0 + } + + if len(reply) < 7 { + return 0 + } + + crc := int64(reply[3]&0x0F)<<12 | + int64(reply[4]&0x0F)<<8 | + int64(reply[5]&0x0F)<<4 | + int64(reply[6]&0x0F) + + return crc +} + +func (a *CAcceptor) GetDeviceFailure() bool { + a.log.Msg("GetDeviceFailure") + return acceptor.Device.State == enum.StateFailed +} + +func (a *CAcceptor) GetDeviceJammed() bool { + a.log.Msg("GetDeviceJammed") + return acceptor.Device.Jammed +} + +func (a *CAcceptor) GetDeviceModel() int { + a.log.Msg("GetDeviceModel") + return acceptor.Device.Model +} + +func (a *CAcceptor) GetDevicePaused() bool { + a.log.Msg("GetDevicePaused") + return acceptor.Device.Paused +} + +func (a *CAcceptor) GetDevicePortName() string { + a.log.Msg("GetDevicePortName") + return a.port.PortName +} + +func (a *CAcceptor) GetDevicePowerUp() enum.PowerUpType { + a.log.Msg("GetDevicePowerUp") + return acceptor.Device.PowerUp +} + +func (a *CAcceptor) GetDeviceResets() int { + a.log.Msg("GetDeviceResets") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.DeviceResets, "DeviceResets") + if err != nil { + a.log.Err("GetDeviceResets", err) + return 0 + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxDeviceResets.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetDeviceResets", err) + return 0 + } + + if len(reply) < 9 { + return 0 + } + + resets := int(reply[3]&0x0F)<<20 | + int(reply[4]&0x0F)<<16 | + int(reply[5]&0x0F)<<12 | + int(reply[6]&0x0F)<<8 | + int(reply[7]&0x0F)<<4 | + int(reply[8]&0x0F) + + return resets +} + +func (a *CAcceptor) GetDeviceRevision() int { + a.log.Msg("GetDeviceRevision") + return acceptor.Device.Revision +} + func (a *CAcceptor) GetDeviceSerialNumber() string { - err := a.verifyPropertyIsAllowed(a.capDeviceSerialNumber, "DeviceSerialNumber") + a.log.Msg("GetDeviceSerialNumber") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.DeviceSerialNumber, "DeviceSerialNumber") if err != nil { - a.log.Debug().Err(err).Msg("GetDeviceSerialNumber") + a.log.Err("GetDeviceSerialNumber", err) return "" } - payload := []byte{CmdAuxiliary, 0, 0, CmdAuxQueryAcceptorSerialNumber} + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorSerialNumber.Byte()} reply, err := a.SendSynchronousCommand(payload) if err != nil { - a.log.Debug().Err(err).Msg("GetDeviceSerialNumber") + a.log.Err("GetDeviceSerialNumber", err) return "" } @@ -45,3 +657,305 @@ func (a *CAcceptor) GetDeviceSerialNumber() string { s := string(reply[3 : 3+returnedStringLength]) return s } + +func (a *CAcceptor) GetDeviceStalled() bool { + a.log.Msg("GetDeviceStalled") + return acceptor.Device.Stalled +} + +func (a *CAcceptor) GetDeviceState() enum.StateType { + a.log.Msg("GetDeviceState") + return acceptor.Device.State +} + +func (a *CAcceptor) GetDeviceType() string { + a.log.Msg("GetDeviceType") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.DeviceType, "DeviceType") + if err != nil { + a.log.Err("GetDeviceType", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorType.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetDeviceType", err) + return "" + } + + // Specified to check for non-printable characters from 0x00 to 0x1F or 0x7F as termination. + validCharIndex := 3 + for ; validCharIndex < len(reply) && reply[validCharIndex] > 0x20 && reply[validCharIndex] < 0x7F && validCharIndex <= 22; validCharIndex++ { + } + + returnedStringLength := validCharIndex - 3 + if returnedStringLength > 0 { + return string(reply[3 : 3+returnedStringLength]) + } + + return "" +} + +func (a *CAcceptor) GetDocType() enum.DocumentType { + a.log.Msg("GetDocType") + return a.docType +} + +func (a *CAcceptor) GetTransactionTimeout() time.Duration { + a.log.Msg("GetTransactionTimeout") + return acceptor.Timeout.Transaction +} + +func (a *CAcceptor) SetTransactionTimeout(v time.Duration) { + a.log.Msg("SetTransactionTimeout") + acceptor.Timeout.Transaction = v +} + +func (a *CAcceptor) GetDownloadTimeout() time.Duration { + a.log.Msg("GetDownloadTimeout") + return acceptor.Timeout.Download +} + +func (a *CAcceptor) SetDownloadTimeout(v time.Duration) { + a.log.Msg("SetDownloadTimeout") + acceptor.Timeout.Download = v +} + +func (a *CAcceptor) GetEnableAcceptance() bool { + a.log.Msg("GetEnableAcceptance") + return acceptor.Enable.Acceptance +} + +func (a *CAcceptor) SetEnableAcceptance(v bool) { + a.log.Msg("SetEnableAcceptance") + acceptor.Enable.Acceptance = v +} + +func (a *CAcceptor) GetEnableBarCodes() bool { + a.log.Msg("GetEnableBarCodes") + return acceptor.Enable.BarCodes +} + +func (a *CAcceptor) SetEnableBarCodes(v bool) { + a.log.Msg("SetEnableBarCodes") + acceptor.Enable.BarCodes = v +} + +func (a *CAcceptor) GetEnableBookmarks() bool { + a.log.Msg("GetEnableBookmarks") + return acceptor.Enable.Bookmarks +} + +func (a *CAcceptor) SetEnableBookmarks(v bool) { + a.log.Msg("SetEnableBookmarks") + acceptor.Enable.Bookmarks = v +} + +func (a *CAcceptor) GetEnableCouponExt() bool { + a.log.Msg("GetEnableCouponExt") + return acceptor.Enable.CouponExt +} + +func (a *CAcceptor) SetEnableCouponExt(v bool) { + a.log.Msg("SetEnableCouponExt") + acceptor.Enable.CouponExt = v +} + +func (a *CAcceptor) GetEnableNoPush() bool { + a.log.Msg("GetEnableNoPush") + return acceptor.Enable.NoPush +} + +func (a *CAcceptor) SetEnableNoPush(v bool) { + a.log.Msg("SetEnableNoPush") + acceptor.Enable.NoPush = v +} + +func (a *CAcceptor) GetEscrowOrientation() enum.OrientationType { + a.log.Msg("GetEscrowOrientation") + if acceptor.Cap.OrientationExt { + return acceptor.EscrowOrientation + } + return enum.OrientationUnknownOrientation +} + +func (a *CAcceptor) GetHighSecurity() bool { + a.log.Msg("GetHighSecurity") + return acceptor.HighSecurity +} + +func (a *CAcceptor) SetHighSecurity(v bool) { + a.log.Msg("SetHighSecurity") + acceptor.HighSecurity = v +} + +func (a *CAcceptor) GetOrientationControl() enum.OrientationControlType { + a.log.Msg("GetOrientationControl") + return acceptor.OrientationCtl +} + +func (a *CAcceptor) SetOrientationControl(v enum.OrientationControlType) { + a.log.Msg("SetOrientationControl") + acceptor.OrientationCtl = v +} + +func (a *CAcceptor) GetOrientationCtlExt() enum.OrientationControlType { + a.log.Msg("GetOrientationCtlExt") + return acceptor.OrientationCtlExt +} + +func (a *CAcceptor) SetOrientationCtlExt(v enum.OrientationControlType) { + a.log.Msg("SetOrientationCtlExt") + acceptor.OrientationCtlExt = v +} + +func (a *CAcceptor) GetVariantNames() []string { + a.log.Msg("GetVariantNames") + + err := a.verifyPropertyIsAllowed(true, "VariantNames") + if err != nil { + a.log.Err("GetVariantNames", err) + return nil + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorVariantName.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetVariantNames", err) + return nil + } + + var names []string + validCharIndex := 3 + + for validCharIndex < len(reply) && reply[validCharIndex] > 0x20 && reply[validCharIndex] < 0x7F && validCharIndex <= 34 { + if validCharIndex+2 < len(reply) { + names = append(names, string(reply[validCharIndex:validCharIndex+3])) + validCharIndex += 4 + } else { + break + } + } + + return names +} + +func (a *CAcceptor) GetVariantID() string { + a.log.Msg("GetVariantID") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.VariantID, "VariantID") + if err != nil { + a.log.Err("GetVariantID", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorVariantID.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetVariantID", err) + return "" + } + + if len(reply) == 14 { + return string(reply[3:12]) // Extracting a 9-byte string starting from index 3 + } + + return "" +} + +func (a *CAcceptor) GetVariantPN() string { + a.log.Msg("GetVariantPN") + + err := a.verifyPropertyIsAllowed(acceptor.Cap.VariantPN, "VariantPN") + if err != nil { + a.log.Err("GetVariantPN", err) + return "" + } + + payload := []byte{consts.CmdAuxiliary.Byte(), 0, 0, consts.CmdAuxAcceptorVariantPartNumber.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("GetVariantPN", err) + return "" + } + + if len(reply) == 14 { + return string(reply[3:12]) + } + + return "" +} + +func (a *CAcceptor) GetVersion() string { + a.log.Msg("GetVersion") + return acceptor.Version +} + +func (a *CAcceptor) Calibrate() { + a.log.Msg("Calibrate") + if !acceptor.Connected { + a.log.Err("Calibrate", errors.New("Calibrate called when not connected")) + return + } + + if acceptor.Device.State != enum.StateIdling { + a.log.Err("Calibrate", errors.New("Calibrate allowed only when DeviceState == Idling")) + return + } + + payload := []byte{consts.CmdCalibrate.Byte(), 0x00, 0x00, 0x00} + + acceptor.SuppressStandardPoll = true + acceptor.Device.State = enum.StateCalibrateStart + + a.RaiseCalibrateStartEvent() + + acceptor.ShouldRaise.CalibrateProgressEvent = true + + startTickCount := time.Now() + + for { + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("Calibrate", errors.New("Failed to send synchronous command during calibration")) + return + } + + if len(reply) == 11 && (reply[2]&0x70) == 0x40 { + break + } + + if time.Since(startTickCount) > CalibrateTimeout { + a.RaiseCalibrateFinishEvent() + return + } + } +} + +//// + +func (a *CAcceptor) ClearCashBoxTotal() (err error) { + a.log.Msg("ClearCashBoxTotal") + + if !acceptor.Connected { + err = errors.New("ClearCashBoxTotal called when not connected") + a.log.Err("ClearCashBoxTotal", err) + return + } + + payload := []byte{consts.CmdCalibrate.Byte(), 0x00, 0x00, consts.CmdAuxCashBoxTotal.Byte()} + + reply, err := a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("ClearCashBoxTotal", err) + return + } + + a.dataLinkLayer.ProcessReply(reply) + return +} diff --git a/acceptor_poll.go b/acceptor_poll.go deleted file mode 100644 index 7ee6689..0000000 --- a/acceptor_poll.go +++ /dev/null @@ -1,19 +0,0 @@ -package mpost - -//////////////////////////////////// - -type CSuppressStandardPoll struct { - acceptor *CAcceptor -} - -func NewCSuppressStandardPoll(acceptor *CAcceptor) *CSuppressStandardPoll { - s := &CSuppressStandardPoll{ - acceptor: acceptor, - } - s.acceptor.suppressStandardPoll = true - return s -} - -func (s *CSuppressStandardPoll) Release() { - s.acceptor.suppressStandardPoll = false -} diff --git a/acceptor_process.go b/acceptor_process.go index d44410a..5878d6e 100644 --- a/acceptor_process.go +++ b/acceptor_process.go @@ -1,213 +1,219 @@ package mpost +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/bill" + "github.com/hard-soft-ware/mpost/enum" +) + //////////////////////////////////// func (a *CAcceptor) processData0(data0 byte) { if (data0 & 0x01) != 0 { - if a.deviceState != Calibrating && a.deviceState != CalibrateStart { - a.deviceState = Idling + if acceptor.Device.State != enum.StateCalibrating && acceptor.Device.State != enum.StateCalibrateStart { + acceptor.Device.State = enum.StateIdling } } if (data0 & 0x02) != 0 { - if a.deviceState != Calibrating && a.deviceState != CalibrateStart { - a.deviceState = Accepting + if acceptor.Device.State != enum.StateCalibrating && acceptor.Device.State != enum.StateCalibrateStart { + acceptor.Device.State = enum.StateAccepting } } if (data0 & 0x04) != 0 { - a.deviceState = Escrow - if a.autoStack { - a.shouldRaiseEscrowEvent = false + acceptor.Device.State = enum.StateEscrow + if acceptor.AutoStack { + acceptor.ShouldRaise.EscrowEvent = false } } else { - a.shouldRaiseEscrowEvent = true + acceptor.ShouldRaise.EscrowEvent = true } if (data0 & 0x08) != 0 { - a.deviceState = Stacking + acceptor.Device.State = enum.StateStacking } if (data0 & 0x10) != 0 { - a.deviceState = Stacked + acceptor.Device.State = enum.StateStacked } else { - a.shouldRaiseStackedEvent = true + acceptor.ShouldRaise.StackedEvent = true } if (data0 & 0x20) != 0 { - a.deviceState = Returning + acceptor.Device.State = enum.StateReturning } if (data0 & 0x40) != 0 { - a.deviceState = Returned - a.bill = CBill{} // Resetting the bill + acceptor.Device.State = enum.StateReturned + bill.Reset() // Resetting the bill } else { - a.shouldRaiseReturnedEvent = true + acceptor.ShouldRaise.ReturnedEvent = true } } func (a *CAcceptor) processData1(data1 byte) { if (data1 & 0x01) != 0 { - a.isCheated = true + acceptor.IsCheated = true } else { - a.isCheated = false - a.shouldRaiseCheatedEvent = true + acceptor.IsCheated = false + acceptor.ShouldRaise.CheatedEvent = true } if (data1 & 0x02) != 0 { - a.deviceState = Rejected + acceptor.Device.State = enum.StateRejected } else { - a.shouldRaiseRejectedEvent = true + acceptor.ShouldRaise.RejectedEvent = true } if (data1 & 0x04) != 0 { - a.isDeviceJammed = true - a.shouldRaiseJamDetectedEvent = true + acceptor.Device.Jammed = true + acceptor.ShouldRaise.JamDetectedEvent = true } else { - a.isDeviceJammed = false - a.shouldRaiseJamClearedEvent = true + acceptor.Device.Jammed = false + acceptor.ShouldRaise.JamClearedEvent = true } if (data1 & 0x08) != 0 { - a.cashBoxFull = true + acceptor.Cash.BoxFull = true } else { - a.cashBoxFull = false - a.shouldRaiseStackerFullEvent = true + acceptor.Cash.BoxFull = false + acceptor.ShouldRaise.StackerFullEvent = true } - a.cashBoxAttached = (data1 & 0x10) != 0 + acceptor.Cash.BoxAttached = (data1 & 0x10) != 0 - if !a.cashBoxAttached { + if !acceptor.Cash.BoxAttached { // Assume a DocumentType exists that handles this // _docType = NoValue } if (data1 & 0x20) != 0 { - a.devicePaused = true - a.shouldRaisePauseClearedEvent = true + acceptor.Device.Paused = true + acceptor.ShouldRaise.PauseClearedEvent = true } else { - a.devicePaused = false - a.shouldRaisePauseDetectedEvent = true + acceptor.Device.Paused = false + acceptor.ShouldRaise.PauseDetectedEvent = true } if (data1 & 0x40) != 0 { - a.deviceState = Calibrating - if a.shouldRaiseCalibrateProgressEvent { + acceptor.Device.State = enum.StateCalibrating + if acceptor.ShouldRaise.CalibrateProgressEvent { a.RaiseCalibrateProgressEvent() } } else { - if a.deviceState == Calibrating { - a.shouldRaiseCalibrateFinishEvent = true - a.deviceState = Idling + if acceptor.Device.State == enum.StateCalibrating { + acceptor.ShouldRaise.CalibrateFinishEvent = true + acceptor.Device.State = enum.StateIdling } } } func (a *CAcceptor) processData2(data2 byte) { - if !a.expandedNoteReporting { + if !acceptor.ExpandedNoteReporting { billTypeIndex := (data2 & 0x38) >> 3 if billTypeIndex > 0 { - if a.deviceState == Escrow || (a.deviceState == Stacked && !a.wasDocTypeSetOnEscrow) { - a.bill = a.billTypes[billTypeIndex-1] - a.docType = Bill - a.wasDocTypeSetOnEscrow = a.deviceState == Escrow + if acceptor.Device.State == enum.StateEscrow || (acceptor.Device.State == enum.StateStacked && !acceptor.WasDocTypeSetOnEscrow) { + bill.Bill = bill.Types[billTypeIndex-1] + a.docType = enum.DocumentBill + acceptor.WasDocTypeSetOnEscrow = acceptor.Device.State == enum.StateEscrow } } else { - if a.deviceState == Stacked || a.deviceState == Escrow { - a.bill = CBill{} - a.docType = NoValue - a.wasDocTypeSetOnEscrow = false + if acceptor.Device.State == enum.StateStacked || acceptor.Device.State == enum.StateEscrow { + bill.Reset() + a.docType = enum.DocumentNoValue + acceptor.WasDocTypeSetOnEscrow = false } } } else { - if a.deviceState == Stacked { - if a.docType == Bill && a.bill.Value == 0.0 { - a.docType = NoValue + if acceptor.Device.State == enum.StateStacked { + if a.docType == enum.DocumentBill && bill.Bill.Value == 0.0 { + a.docType = enum.DocumentNoValue } - } else if a.deviceState == Escrow { - a.bill = CBill{} - a.docType = NoValue + } else if acceptor.Device.State == enum.StateEscrow { + bill.Reset() + a.docType = enum.DocumentNoValue } } if (data2 & 0x01) != 0 { - a.isPoweredUp = true - a.docType = NoValue + acceptor.IsPoweredUp = true + a.docType = enum.DocumentNoValue } else { - a.shouldRaisePowerUpEvent = true - if !a.isVeryFirstPoll { - a.isPoweredUp = false + acceptor.ShouldRaise.PowerUpEvent = true + if !acceptor.IsVeryFirstPoll { + acceptor.IsPoweredUp = false } } if (data2 & 0x02) != 0 { - a.isInvalidCommand = true + acceptor.IsInvalidCommand = true } else { - a.isInvalidCommand = false - a.shouldRaiseInvalidCommandEvent = true + acceptor.IsInvalidCommand = false + acceptor.ShouldRaise.InvalidCommandEvent = true } if (data2 & 0x04) != 0 { - a.deviceState = Failed + acceptor.Device.State = enum.StateFailed } } func (a *CAcceptor) processData3(data3 byte) { if (data3 & 0x01) != 0 { - a.deviceState = Stalled - a.shouldRaiseStallClearedEvent = true + acceptor.Device.State = enum.StateStalled + acceptor.ShouldRaise.StallClearedEvent = true } else { - a.shouldRaiseStallDetectedEvent = true + acceptor.ShouldRaise.StallDetectedEvent = true } if (data3 & 0x02) != 0 { - a.deviceState = DownloadRestart + acceptor.Device.State = enum.StateDownloadRestart } if (data3 & 0x08) != 0 { - a.capBarCodesExt = true + acceptor.Cap.BarCodesExt = true } if (data3 & 0x10) != 0 { - a.isQueryDeviceCapabilitiesSupported = true + acceptor.IsQueryDeviceCapabilitiesSupported = true } } func (a *CAcceptor) processData4(data4 byte) { - a.deviceModel = int(data4 & 0x7F) //todo проверить валидность перевода - m := a.deviceModel + acceptor.Device.Model = int(data4 & 0x7F) + m := acceptor.Device.Model d := m - a.capApplicationPN = m == 'T' || m == 'U' - a.capAssetNumber = m == 'T' || m == 'U' - a.capAudit = m == 'T' || m == 'U' - a.capBarCodes = m == 'T' || m == 'U' || d == 15 || d == 23 - a.capBookmark = true - a.capBootPN = m == 'T' || m == 'U' - a.capCalibrate = true - a.capCashBoxTotal = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'W' || m == 'X' - a.capCouponExt = m == 'P' || m == 'X' - a.capDevicePaused = m == 'P' || m == 'X' || d == 31 - a.capDeviceSoftReset = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'T' || m == 'U' || m == 'W' || m == 'X' || d == 31 - a.capDeviceType = m == 'T' || m == 'U' - a.capDeviceResets = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'T' || m == 'U' || m == 'W' || m == 'X' - a.capDeviceSerialNumber = m == 'T' || m == 'U' - a.capFlashDownload = true - a.capEscrowTimeout = m == 'T' || m == 'U' - a.capNoPush = m == 'P' || m == 'X' || d == 31 || d == 23 - a.capVariantPN = m == 'T' || m == 'U' - a.expandedNoteReporting = m == 'T' || m == 'U' // This setting might be toggled in debug or production builds + acceptor.Cap.ApplicationPN = m == 'T' || m == 'U' + acceptor.Cap.AssetNumber = m == 'T' || m == 'U' + acceptor.Cap.Audit = m == 'T' || m == 'U' + acceptor.Cap.BarCodes = m == 'T' || m == 'U' || d == 15 || d == 23 + acceptor.Cap.Bookmark = true + acceptor.Cap.BootPN = m == 'T' || m == 'U' + acceptor.Cap.Calibrate = true + acceptor.Cap.CashBoxTotal = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'W' || m == 'X' + acceptor.Cap.CouponExt = m == 'P' || m == 'X' + acceptor.Cap.DevicePaused = m == 'P' || m == 'X' || d == 31 + acceptor.Cap.DeviceSoftReset = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'T' || m == 'U' || m == 'W' || m == 'X' || d == 31 + acceptor.Cap.DeviceType = m == 'T' || m == 'U' + acceptor.Cap.DeviceResets = m == 'A' || m == 'B' || m == 'C' || m == 'D' || m == 'G' || m == 'M' || m == 'P' || m == 'T' || m == 'U' || m == 'W' || m == 'X' + acceptor.Cap.DeviceSerialNumber = m == 'T' || m == 'U' + acceptor.Cap.FlashDownload = true + acceptor.Cap.EscrowTimeout = m == 'T' || m == 'U' + acceptor.Cap.NoPush = m == 'P' || m == 'X' || d == 31 || d == 23 + acceptor.Cap.VariantPN = m == 'T' || m == 'U' + acceptor.ExpandedNoteReporting = m == 'T' || m == 'U' // This setting might be toggled in debug or production builds } func (a *CAcceptor) processData5(data5 byte) { switch { - case a.deviceModel < 23, // S1K - a.deviceModel == 30 || a.deviceModel == 31, // S3K - a.deviceModel == 74, // CFMC - a.deviceModel == 84 || a.deviceModel == 85: // CFSC - a.deviceRevision = int(data5 & 0x7F) + case acceptor.Device.Model < 23, // S1K + acceptor.Device.Model == 30 || acceptor.Device.Model == 31, // S3K + acceptor.Device.Model == 74, // CFMC + acceptor.Device.Model == 84 || acceptor.Device.Model == 85: // CFSC + acceptor.Device.Revision = int(data5 & 0x7F) default: // S2K - a.deviceRevision = int(data5&0x0F) + (int(data5&0x70)>>4)*10 + acceptor.Device.Revision = int(data5&0x0F) + (int(data5&0x70)>>4)*10 } } diff --git a/acceptor_raise.go b/acceptor_raise.go index c74a1f8..4d40318 100644 --- a/acceptor_raise.go +++ b/acceptor_raise.go @@ -1,37 +1,173 @@ package mpost +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/enum" +) + //////////////////////////////////// -func (a *CAcceptor) raiseXX(e Event) { +func (a *CAcceptor) raiseXX(e enum.EventType, b int) { + if e != enum.EventJamCleared { + a.log.Event(e) + } + if handler, exists := a.eventHandlers[e]; exists && handler != nil { - handler(a, 0) + handler(a, b) } } //// func (a *CAcceptor) RaiseConnectedEvent() { - a.log.Debug().Str("Event", "ConnectedEvent").Msg("Raise") - a.raiseXX(ConnectedEvent) - a.shouldRaiseDisconnectedEvent = true - a.shouldRaiseConnectedEvent = false + if acceptor.ShouldRaise.ConnectedEvent { + return + } + + a.raiseXX(enum.EventConnected, 0) + acceptor.ShouldRaise.DisconnectedEvent = true + acceptor.ShouldRaise.ConnectedEvent = false } func (a *CAcceptor) RaiseDisconnectedEvent() { - a.log.Debug().Str("Event", "DisconnectedEvent").Msg("Raise") - a.raiseXX(DisconnectedEvent) - a.shouldRaiseDisconnectedEvent = false - a.shouldRaiseConnectedEvent = true + if !acceptor.ShouldRaise.DisconnectedEvent { + return + } + + a.raiseXX(enum.EventDisconnected, 0) + acceptor.ShouldRaise.DisconnectedEvent = false + acceptor.ShouldRaise.ConnectedEvent = true } func (a *CAcceptor) RaiseDownloadRestartEvent() { - a.log.Debug().Str("Event", "DownloadRestartEvent").Msg("Raise") - a.raiseXX(DownloadRestartEvent) - a.shouldRaiseDownloadRestartEvent = false + a.raiseXX(enum.EventDownloadRestart, 0) + acceptor.ShouldRaise.DownloadRestartEvent = false } +// + func (a *CAcceptor) RaiseCalibrateProgressEvent() { - a.log.Debug().Str("Event", "CalibrateProgressEvent").Msg("Raise") - a.raiseXX(CalibrateProgressEvent) - a.shouldRaiseCalibrateProgressEvent = false + a.raiseXX(enum.EventCalibrateProgress, 0) + acceptor.ShouldRaise.CalibrateProgressEvent = false +} + +func (a *CAcceptor) RaiseCalibrateStartEvent() { + a.raiseXX(enum.EventCalibrateStart, 0) + acceptor.ShouldRaise.CalibrateStartEvent = false +} + +func (a *CAcceptor) RaiseCalibrateFinishEvent() { + a.raiseXX(enum.EventCalibrateFinish, 0) + acceptor.ShouldRaise.CalibrateFinishEvent = false +} + +// + +func (a *CAcceptor) RaiseDownloadFinishEvent(st bool) { + v := 0 + if st { + v = 1 + } + a.raiseXX(enum.EventDownloadFinish, v) + acceptor.ShouldRaise.DownloadFinishEvent = false +} + +func (a *CAcceptor) RaiseDownloadStartEvent(v int) { + a.raiseXX(enum.EventDownloadStart, v) + + acceptor.ShouldRaise.DownloadStartEvent = false + acceptor.ShouldRaise.DownloadProgressEvent = true +} + +func (a *CAcceptor) RaiseDownloadProgressEvent(v int) { + a.raiseXX(enum.EventDownloadProgress, v) +} + +// + +func (a *CAcceptor) RaisePowerUpEvent() { + a.raiseXX(enum.EventPowerUp, 0) + acceptor.ShouldRaise.PowerUpEvent = false +} + +func (a *CAcceptor) RaisePUPEscrowEvent() { + a.raiseXX(enum.EventPUPEscrow, 0) + acceptor.ShouldRaise.PUPEscrowEvent = false +} + +func (a *CAcceptor) RaiseEscrowEvent() { + a.raiseXX(enum.EventEscrow, 0) + acceptor.ShouldRaise.EscrowEvent = false +} + +func (a *CAcceptor) RaiseStackedEvent() { + a.raiseXX(enum.EventStacked, 0) + acceptor.ShouldRaise.StackedEvent = false +} + +func (a *CAcceptor) RaiseReturnedEvent() { + a.raiseXX(enum.EventReturned, 0) + acceptor.ShouldRaise.ReturnedEvent = false +} + +func (a *CAcceptor) RaiseRejectedEvent() { + a.raiseXX(enum.EventRejected, 0) + acceptor.ShouldRaise.RejectedEvent = false +} + +func (a *CAcceptor) RaiseStallDetectedEvent() { + a.raiseXX(enum.EventStallDetected, 0) + acceptor.ShouldRaise.StallDetectedEvent = false +} + +func (a *CAcceptor) RaiseStallClearedEvent() { + a.raiseXX(enum.EventStallCleared, 0) + acceptor.ShouldRaise.StallClearedEvent = false +} + +func (a *CAcceptor) RaiseStackerFullEvent() { + a.raiseXX(enum.EventStackerFull, 0) + acceptor.ShouldRaise.StackerFullEvent = false +} + +func (a *CAcceptor) RaiseCheatedEvent() { + a.raiseXX(enum.EventCheated, 0) + acceptor.ShouldRaise.CheatedEvent = false +} + +func (a *CAcceptor) RaiseCashBoxAttachedEvent() { + a.raiseXX(enum.EventCashBoxAttached, 0) + acceptor.ShouldRaise.CashBoxAttachedEvent = false + acceptor.ShouldRaise.CashBoxRemovedEvent = true +} + +func (a *CAcceptor) RaiseCashBoxRemovedEvent() { + a.raiseXX(enum.EventCashBoxRemoved, 0) + acceptor.ShouldRaise.CashBoxRemovedEvent = false + acceptor.ShouldRaise.CashBoxAttachedEvent = true +} + +func (a *CAcceptor) RaisePauseDetectedEvent() { + a.raiseXX(enum.EventPauseDetected, 0) + acceptor.ShouldRaise.PauseDetectedEvent = false +} + +func (a *CAcceptor) RaisePauseClearedEvent() { + a.raiseXX(enum.EventPauseCleared, 0) + acceptor.ShouldRaise.PauseClearedEvent = false +} + +func (a *CAcceptor) RaiseJamDetectedEvent() { + a.raiseXX(enum.EventJamDetected, 0) + acceptor.ShouldRaise.JamDetectedEvent = false +} + +func (a *CAcceptor) RaiseJamClearedEvent() { + a.raiseXX(enum.EventJamCleared, 0) + acceptor.ShouldRaise.JamClearedEvent = false +} + +func (a *CAcceptor) RaiseInvalidCommandEvent() { + a.raiseXX(enum.EventInvalidCommand, 0) + acceptor.ShouldRaise.InvalidCommandEvent = false } diff --git a/acceptor_serial.go b/acceptor_serial.go index c6313a5..556e6b2 100644 --- a/acceptor_serial.go +++ b/acceptor_serial.go @@ -2,40 +2,29 @@ package mpost import ( "fmt" - "go.bug.st/serial" - "strings" - "time" + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" + "github.com/hard-soft-ware/mpost/serial" ) -func byteSliceToString(b []byte) string { - var sb strings.Builder - for i, byteVal := range b { - if i > 0 { - sb.WriteString(" ") - } - fmt.Fprintf(&sb, "%02X", byteVal) - } - return sb.String() -} - //////////////////////////////////// -func (a *CAcceptor) Open(portName string, powerUp PowerUp) error { - lg := a.log.NewLog("OpenSerial") - - if a.connected { - lg.Debug().Msg("already connected") +func (a *CAcceptor) Open(portName string, powerUp enum.PowerUpType) error { + if acceptor.Connected { + a.log.Msg("already connected") return nil } - a.devicePortName = portName - a.devicePowerUp = powerUp + acceptor.Device.PowerUp = powerUp - err := a.OpenPort(lg) + port, err := serial.Open(portName, &acceptor.Connected) if err != nil { - lg.Debug().Err(err).Msg("failed to open serial port") + a.log.Err("failed to open serial port", err) return err } + a.port = port + a.log.Msg("Serial Open") go a.MessageLoopThread() go a.OpenThread() @@ -43,91 +32,61 @@ func (a *CAcceptor) Open(portName string, powerUp PowerUp) error { return nil } -func (a *CAcceptor) OpenPort(lg *LogGlobalStruct) error { - mode := &serial.Mode{ - BaudRate: 9600, - DataBits: 7, - Parity: serial.EvenParity, - StopBits: serial.OneStopBit, - } - - port, err := serial.Open(a.devicePortName, mode) - if err != nil { - lg.Debug().Err(err).Msg("failed to open serial port") - return err - } - - port.SetReadTimeout(100 * time.Millisecond) - port.ResetInputBuffer() - - port.SetDTR(false) - port.SetRTS(true) - time.Sleep(100 * time.Millisecond) - - port.SetDTR(true) - port.SetRTS(false) - time.Sleep(5 * time.Millisecond) - - port.ResetInputBuffer() - a.port = port - - a.connected = true - lg.Debug().Msg("Connected") - return nil -} - func (a *CAcceptor) Close() { + a.RaiseDisconnectedEvent() + a.port.Close() - if !a.connected { - a.stopOpenThread = true + defer a.CtxCancel() + + if !acceptor.Connected { + acceptor.StopOpenThread = true return } - if a.enableAcceptance { - a.enableAcceptance = false + if acceptor.Enable.Acceptance { + acceptor.Enable.Acceptance = false } if a.dataLinkLayer != nil { - a.dataLinkLayer.FlushIdenticalTransactionsToLog() + a.log.Msg(fmt.Sprintf("IdenticalCommandAndReplyCount: %d", a.dataLinkLayer.IdenticalCommandAndReplyCount)) } - a.stopWorkerThread = true - a.workerThread.Wait() + acceptor.StopWorkerThread = true - a.port.Close() a.port = nil - a.connected = false + acceptor.Connected = false + a.log.Msg("Close") } //// -func (a *CAcceptor) QueryDeviceCapabilities(lg *LogGlobalStruct) { - if !a.isQueryDeviceCapabilitiesSupported { +func (a *CAcceptor) QueryDeviceCapabilities() { + if !acceptor.IsQueryDeviceCapabilitiesSupported { return } - payload := []byte{CmdAuxiliary, 0x00, 0x00, CmdAuxQueryDeviceCapabilities} + payload := []byte{consts.CmdAuxiliary.Byte(), 0x00, 0x00, consts.CmdAuxDeviceCapabilities.Byte()} reply, err := a.SendSynchronousCommand(payload) if len(reply) < 4 { - lg.Debug().Err(err).Msg("Reply too short, unable to process.") + a.log.Err("Reply too short, unable to process.", err) return } if reply[3]&0x01 != 0 { - a.capPupExt = true + acceptor.Cap.PupExt = true } if reply[3]&0x02 != 0 { - a.capOrientationExt = true + acceptor.Cap.OrientationExt = true } if reply[3]&0x04 != 0 { - a.capApplicationID = true - a.capVariantID = true + acceptor.Cap.ApplicationID = true + acceptor.Cap.VariantID = true } if reply[3]&0x08 != 0 { - a.capBNFStatus = true + acceptor.Cap.BNFStatus = true } if reply[3]&0x10 != 0 { - a.capTestDoc = true + acceptor.Cap.TestDoc = true } } diff --git a/acceptor_thread.go b/acceptor_thread.go index 69ffaf0..0f4ee6b 100644 --- a/acceptor_thread.go +++ b/acceptor_thread.go @@ -1,28 +1,32 @@ package mpost -import "time" +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/bill" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" + "time" +) //////////////////////////////////// func (a *CAcceptor) OpenThread() { - lg := a.log.NewLog("Thread") + lg := a.log.New("Thread") - replay := a.PollingLoop(lg) + replay := a.PollingLoop() - if a.wasStopped { - lg.Debug().Msg("thread is stopped") + if acceptor.WasStopped { + lg.Msg("thread is stopped") return } a.dataLinkLayer.ProcessReply(replay) - a.QueryDeviceCapabilities(lg) + a.QueryDeviceCapabilities() - if a.deviceState != DownloadRestart { + if acceptor.Device.State != enum.StateDownloadRestart { a.SetUpBillTable() - a.connected = true - if a.shouldRaiseConnectedEvent { - a.RaiseConnectedEvent() - } + acceptor.Connected = true + a.RaiseConnectedEvent() } else { a.RaiseDownloadRestartEvent() } @@ -31,50 +35,53 @@ func (a *CAcceptor) OpenThread() { //// func (a *CAcceptor) MessageLoopThread() { - lg := a.log.NewLog("LoopThread") + lg := a.log.New("LoopThread") a.dataLinkLayer = a.NewCDataLinkLayer(lg) timeoutStart := time.Now() + loopCycleCounter := 0 for { - if !a.inSoftResetWaitForReply { + if !acceptor.InSoftResetWaitForReply { time.Sleep(10 * time.Millisecond) } else { time.Sleep(1000 * time.Millisecond) } if time.Since(timeoutStart) > 30*time.Second { - if a.deviceState != Downloading && a.deviceState != DownloadRestart { - a.connected = false - if a.shouldRaiseDisconnectedEvent { - a.RaiseDisconnectedEvent() - } - a.wasDisconnected = true + if acceptor.Device.State != enum.StateDownloading && acceptor.Device.State != enum.StateDownloadRestart { + acceptor.Connected = false + a.Close() + acceptor.WasDisconnected = true timeoutStart = time.Now() } } - if a.stopWorkerThread { - a.stopWorkerThread = false - lg.Debug().Msg("thread is stopped") + if acceptor.StopWorkerThread { + acceptor.StopWorkerThread = false + lg.Msg("thread is stopped") return } select { + case <-a.Ctx.Done(): + a.Close() + return + case message := <-a.messageQueue: - lg.Debug().Bytes("payload", message.Payload).Msg("MessageLoopThread") + loopCycleCounter = 0 a.dataLinkLayer.SendPacket(message.Payload) reply, err := a.dataLinkLayer.ReceiveReply() if err != nil { - lg.Error().Err(err).Msg("Invalid ReceiveReply") + a.log.Err("Invalid ReceiveReply", err) continue } if len(reply) > 0 { timeoutStart = time.Now() - if a.wasDisconnected { - a.wasDisconnected = false + if acceptor.WasDisconnected { + acceptor.WasDisconnected = false a.RaiseConnectedEvent() } if message.IsSynchronous { @@ -83,6 +90,46 @@ func (a *CAcceptor) MessageLoopThread() { a.dataLinkLayer.ProcessReply(reply) } } + + default: + loopCycleCounter++ + + if loopCycleCounter > 9 { + loopCycleCounter = 0 + + payload := make([]byte, 4) + acceptor.ConstructOmnibusCommand(payload, consts.CmdOmnibus, 1, bill.TypeEnables) + + a.dataLinkLayer.SendPacket(payload) + + reply, err := a.dataLinkLayer.ReceiveReply() + if err != nil { + a.Close() + a.log.Err("Invalid loopCycleCounter", err) + return + } + + if len(reply) > 0 { + timeoutStart = time.Now() + + if acceptor.WasDisconnected { + acceptor.WasDisconnected = false + + if reply[2]&0x70 != 0x50 { + acceptor.Connected = true + a.RaiseConnectedEvent() + } else { + a.RaiseDownloadRestartEvent() + } + } + + if acceptor.InSoftResetWaitForReply { + acceptor.InSoftResetWaitForReply = false + } + + a.dataLinkLayer.ProcessReply(reply) + } + } } } } diff --git a/bill.go b/bill.go index 0295f94..e50f311 100644 --- a/bill.go +++ b/bill.go @@ -1,25 +1,19 @@ package mpost import ( - "fmt" + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/bill" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" "strconv" "strings" + "time" ) //////////////////////////////////// -// представление купюры -type CBill struct { - Country string - Value float64 - Type rune - Series rune - Compatibility rune - Version rune -} - -func (a *CAcceptor) ParseBillData(reply []byte, extDataIndex int) CBill { - var bill CBill +func (a *CAcceptor) ParseBillData(reply []byte, extDataIndex int) bill.BillStruct { + var bill bill.BillStruct if len(reply) < extDataIndex+15 { return bill @@ -52,19 +46,19 @@ func (a *CAcceptor) ParseBillData(reply []byte, extDataIndex int) CBill { } bill.Value = billValue - a.docType = Bill - a.wasDocTypeSetOnEscrow = a.deviceState == Escrow + a.docType = enum.DocumentBill + acceptor.WasDocTypeSetOnEscrow = acceptor.Device.State == enum.StateEscrow orientation := reply[extDataIndex+10] switch orientation { case 0x00: - a.escrowOrientation = RightUp + acceptor.EscrowOrientation = enum.OrientationRightUp case 0x01: - a.escrowOrientation = RightDown + acceptor.EscrowOrientation = enum.OrientationRightDown case 0x02: - a.escrowOrientation = LeftUp + acceptor.EscrowOrientation = enum.OrientationLeftUp case 0x03: - a.escrowOrientation = LeftDown + acceptor.EscrowOrientation = enum.OrientationLeftDown } bill.Type = rune(reply[extDataIndex+11]) @@ -75,32 +69,59 @@ func (a *CAcceptor) ParseBillData(reply []byte, extDataIndex int) CBill { return bill } -//// +//////// + +func (a *CAcceptor) RetrieveBillTable() { + var index byte = 1 + for { + payload := make([]byte, 6) + acceptor.ConstructOmnibusCommand(payload, consts.CmdExpanded, 2, bill.TypeEnables) + payload[1] = 0x02 + payload[5] = index + + var reply []byte + var err error + { + for { + reply, err = a.SendSynchronousCommand(payload) + if err != nil { + a.log.Err("Error sending command", err) + break + } + if len(reply) == 30 { + break + } + time.Sleep(100 * time.Millisecond) + } + } -func (b *CBill) ToString() string { - return fmt.Sprintf("%s %.2f %c %c %c %c", b.Country, b.Value, b.Series, b.Type, b.Compatibility, b.Version) -} + if err != nil || len(reply) != 30 { + break + } -func (b *CBill) GetCountry() string { - return b.Country -} + ctl := reply[2] + if (ctl&0x70) != 0x70 || reply[3] != 0x02 { + break + } -func (b *CBill) GetValue() float64 { - return b.Value -} + if reply[10] == 0 { + break + } -func (b *CBill) GetSeries() rune { - return b.Series -} + billFromTable := a.ParseBillData(reply, 10) + bill.Types = append(bill.Types, billFromTable) + index++ + } -func (b *CBill) GetType() rune { - return b.Type -} + for range bill.Types { + bill.TypeEnables = append(bill.TypeEnables, true) + } -func (b *CBill) GetCompatibility() rune { - return b.Compatibility + a.log.Msg("Bill table retrieved") } -func (b *CBill) GetVersion() rune { - return b.Version +func (a *CAcceptor) SetUpBillTable() { + bill.SetUpTable(acceptor.ExpandedNoteReporting, func() { + a.RetrieveBillTable() + }) } diff --git a/bill/bill.go b/bill/bill.go new file mode 100644 index 0000000..917da4f --- /dev/null +++ b/bill/bill.go @@ -0,0 +1,56 @@ +package bill + +import "fmt" + +//////////////////////////////////// + +type BillStruct struct { + Country string + Value float64 + Type rune + Series rune + Compatibility rune + Version rune +} + +var Bill BillStruct + +var Types []BillStruct +var TypeEnables []bool + +var Values []BillStruct +var ValueEnables []bool + +//// + +func Reset() { + Bill = BillStruct{} +} + +func (b *BillStruct) ToString() string { + return fmt.Sprintf("%s %.2f %c %c %c %c", b.Country, b.Value, b.Series, b.Type, b.Compatibility, b.Version) +} + +func (b *BillStruct) GetCountry() string { + return b.Country +} + +func (b *BillStruct) GetValue() float64 { + return b.Value +} + +func (b *BillStruct) GetSeries() rune { + return b.Series +} + +func (b *BillStruct) GetType() rune { + return b.Type +} + +func (b *BillStruct) GetCompatibility() rune { + return b.Compatibility +} + +func (b *BillStruct) GetVersion() rune { + return b.Version +} diff --git a/bill/table.go b/bill/table.go new file mode 100644 index 0000000..959dce5 --- /dev/null +++ b/bill/table.go @@ -0,0 +1,151 @@ +package bill + +import ( + "github.com/hard-soft-ware/mpost/acceptor" +) + +//////////////////////////////////// + +func ClearTable() { + Types = []BillStruct{} + TypeEnables = []bool{} + Values = []BillStruct{} + ValueEnables = []bool{} +} + +func BuildHardCodedTable() { + Types = []BillStruct{} + + switch acceptor.Device.Model { + case 1, 12, 23, 30, 31, 'J', 'X', 'T': + Types = append(Types, BillStruct{"USD", 1, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 2, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 100, '*', '*', '*', '*'}) + + case 'P': + Types = append(Types, BillStruct{"USD", 1, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 2, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 20, '*', '*', '*', '*'}) + + case 'G': + Types = append(Types, BillStruct{}) + Types = append(Types, BillStruct{"ARS", 2, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"ARS", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"ARS", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"ARS", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"ARS", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"ARS", 100, '*', '*', '*', '*'}) + + case 'A': + Types = append(Types, BillStruct{"AUD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 100, '*', '*', '*', '*'}) + + case 15: + Types = append(Types, BillStruct{}) + Types = append(Types, BillStruct{}) + Types = append(Types, BillStruct{"AUD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"AUD", 100, '*', '*', '*', '*'}) + + case 'W': + Types = append(Types, BillStruct{"BRL", 1, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 2, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"BRL", 100, '*', '*', '*', '*'}) + + case 'C': + Types = append(Types, BillStruct{}) + Types = append(Types, BillStruct{}) + Types = append(Types, BillStruct{"CAD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"CAD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"CAD", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"CAD", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"CAD", 100, '*', '*', '*', '*'}) + + case 'D': + Types = append(Types, BillStruct{"EUR", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"EUR", 10, '*', '*', '*', '*'}) + + case 'M': + Types = append(Types, BillStruct{"MXP", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"MXP", 50, '*', '*', '*', '*'}) + + case 'B': + Types = append(Types, BillStruct{"RUR", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"RUR", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"RUR", 100, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"RUR", 500, '*', '*', '*', '*'}) + + default: + Types = append(Types, BillStruct{"USD", 1, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 2, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 5, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 10, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 20, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 50, '*', '*', '*', '*'}) + Types = append(Types, BillStruct{"USD", 100, '*', '*', '*', '*'}) + } + + TypeEnables = make([]bool, len(Types)) + for i, bill := range Types { + if bill.Value > 0 { + TypeEnables[i] = true + } else { + TypeEnables[i] = false + } + } +} + +func BuildValues() { + Values = []BillStruct{} + ValueEnables = []bool{} + + for i := range Types { + valueExists := false + + for j := range Values { + if Types[i].Value == Values[j].Value && Types[i].Country == Values[j].Country { + valueExists = true + break + } + } + + if !valueExists { + Values = append(Values, BillStruct{ + Country: Types[i].Country, + Value: Types[i].Value, + Type: '*', + Series: '*', + Compatibility: '*', + Version: '*', + }) + ValueEnables = append(ValueEnables, Types[i].Value > 0) + } + } +} + +//// + +func SetUpTable(expandedNoteReporting bool, RetrieveBillTable func()) { + ClearTable() + + if expandedNoteReporting { + RetrieveBillTable() + } else { + BuildHardCodedTable() + } + + BuildValues() +} diff --git a/bill_table.go b/bill_table.go deleted file mode 100644 index 3f41c25..0000000 --- a/bill_table.go +++ /dev/null @@ -1,154 +0,0 @@ -package mpost - -import ( - "time" -) - -//////////////////////////////////// - -func (a *CAcceptor) SetUpBillTable() { - a.ClearBillTable() - - if a.expandedNoteReporting { - a.RetrieveBillTable() - } else { - a.BuildHardCodedBillTable() - } - - a.BuildBillValues() -} - -//// - -func (a *CAcceptor) ClearBillTable() { - a.billTypes = []CBill{} - a.billTypeEnables = []bool{} - a.billValues = []CBill{} - a.billValueEnables = []bool{} - - a.log.Debug().Msg("Bill table cleared") -} - -func (a *CAcceptor) RetrieveBillTable() { - index := 1 - for { - payload := make([]byte, 6) - a.ConstructOmnibusCommand(payload, CmdExpanded, 2) - payload[1] = 0x02 - payload[5] = byte(index) - - var reply []byte - var err error - for { - reply, err = a.SendSynchronousCommand(payload) - if err != nil || len(reply) == 30 { - break - } - time.Sleep(100 * time.Millisecond) - } - - if err != nil || len(reply) != 30 { - a.log.Debug().Err(err).Msg("Error sending command") - break - } - - ctl := reply[2] - if (ctl&0x70) != 0x70 || reply[3] != 0x02 { - break - } - - if reply[10] == 0 { - break - } - - billFromTable := a.ParseBillData(reply, 10) - a.billTypes = append(a.billTypes, billFromTable) - index++ - } - - for range a.billTypes { - a.billTypeEnables = append(a.billTypeEnables, true) - } - - a.log.Debug().Msg("Bill table retrieved") -} - -func (a *CAcceptor) BuildHardCodedBillTable() { - a.billTypes = []CBill{} - - switch a.deviceModel { - case 1, 12, 23, 30, 31, 'J', 'X', 'T': - a.billTypes = append(a.billTypes, CBill{"USD", 1, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 2, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 5, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 10, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 20, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 50, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 100, '*', '*', '*', '*'}) - - case 'P': - a.billTypes = append(a.billTypes, CBill{"USD", 1, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 2, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 5, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 10, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 20, '*', '*', '*', '*'}) - - case 'G': - a.billTypes = append(a.billTypes, CBill{"ARS", 2, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"ARS", 5, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"ARS", 10, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"ARS", 20, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"ARS", 50, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"ARS", 100, '*', '*', '*', '*'}) - - default: - a.billTypes = append(a.billTypes, CBill{"USD", 1, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 2, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 5, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 10, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 20, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 50, '*', '*', '*', '*'}) - a.billTypes = append(a.billTypes, CBill{"USD", 100, '*', '*', '*', '*'}) - } - - a.billTypeEnables = make([]bool, len(a.billTypes)) - for i, bill := range a.billTypes { - if bill.Value > 0 { - a.billTypeEnables[i] = true - } else { - a.billTypeEnables[i] = false - } - } - - a.log.Debug().Msg("Hardcoded bill table built") -} - -func (a *CAcceptor) BuildBillValues() { - a.billValues = []CBill{} - a.billValueEnables = []bool{} - - for i := range a.billTypes { - valueExists := false - - for j := range a.billValues { - if a.billTypes[i].Value == a.billValues[j].Value && a.billTypes[i].Country == a.billValues[j].Country { - valueExists = true - break - } - } - - if !valueExists { - a.billValues = append(a.billValues, CBill{ - Country: a.billTypes[i].Country, - Value: a.billTypes[i].Value, - Type: '*', - Series: '*', - Compatibility: '*', - Version: '*', - }) - a.billValueEnables = append(a.billValueEnables, a.billTypes[i].Value > 0) - } - } - - a.log.Debug().Msg("Bill values built") -} diff --git a/command/command.go b/command/command.go new file mode 100644 index 0000000..211114c --- /dev/null +++ b/command/command.go @@ -0,0 +1,33 @@ +package command + +import ( + "github.com/hard-soft-ware/mpost/consts" +) + +//////////////////////////////////// + +func CRC(command []byte) byte { + var result byte + + end := int(command[1]) - 2 + for i := 1; i < end; i++ { + result ^= command[i] + } + + return result +} + +func Create(payload []byte) []byte { + commandLength := len(payload) + 4 // STX + Length char + ETX + Checksum + + command := make([]byte, 0, commandLength) + command = append(command, consts.DataSTX.Byte()) + command = append(command, byte(commandLength)) + + command = append(command, payload...) + + command = append(command, consts.DataETX.Byte()) + command = append(command, CRC(command)) + + return command +} diff --git a/const.go b/const.go index 6d6cc39..366c2fa 100644 --- a/const.go +++ b/const.go @@ -1,36 +1,12 @@ package mpost -//////////////////////////////////// - -const ( - CmdOmnibus = 0x10 - CmdCalibrate = 0x40 - CmdFlashDownload = 0x50 - CmdAuxiliary = 0x60 - CmdExpanded = 0x70 - - CmdAuxQuerySoftwareCRC = 0x00 - CmdAuxQueryCashBoxTotal = 0x01 - CmdAuxQueryDeviceResets = 0x02 - CmdAuxClearCashBoxTotal = 0x03 - CmdAuxQueryAcceptorType = 0x04 - CmdAuxQueryAcceptorSerialNumber = 0x05 - CmdAuxQueryAcceptorBootPartNumber = 0x06 - CmdAuxQueryAcceptorApplicationPartNumber = 0x07 - CmdAuxQueryAcceptorVariantName = 0x08 - CmdAuxQueryAcceptorVariantPartNumber = 0x09 - CmdAuxQueryAcceptorAuditLifeTimeTotals = 0x0A - CmdAuxQueryAcceptorAuditQPMeasures = 0x0B - CmdAuxQueryAcceptorAuditPerformanceMeasures = 0x0C - CmdAuxQueryDeviceCapabilities = 0x0D - CmdAuxQueryAcceptorApplicationID = 0x0E - CmdAuxQueryAcceptorVariantID = 0x0F - CmdAuxQueryBNFStatus = 0x10 - CmdAuxSetBezel = 0x11 -) - const ( - CommunicationDisconnectTimeout = 3000 - PollingDisconnectTimeout = 3000 - CalibrateTimeout = 3000 + GlobalName string = "mpost" + GlobalDateUpdate string = "09-11-2024" + GlobalHash string = "e33d475848c735d5072a6377e67ec3ccabae2f11" + + GlobalVersion string = "v0.1.16" + GlobalVersionMajor string = "v0" + GlobalVersionMinor uint16 = 1 + GlobalVersionPatch uint16 = 16 ) diff --git a/consts/cmd.go b/consts/cmd.go new file mode 100644 index 0000000..4c987bc --- /dev/null +++ b/consts/cmd.go @@ -0,0 +1,41 @@ +package consts + +/* This file is automatically generated */ + +type CmdType byte + +const ( + CmdOmnibus CmdType = 0x10 + CmdCalibrate CmdType = 0x40 + CmdFlashDownload CmdType = 0x50 + CmdAuxiliary CmdType = 0x60 + CmdExpanded CmdType = 0x70 +) + +const ( + CmdTextOmnibus = "Omnibus" + CmdTextCalibrate = "Calibrate" + CmdTextFlashDownload = "FlashDownload" + CmdTextAuxiliary = "Auxiliary" + CmdTextExpanded = "Expanded" +) + +var CmdMap = map[CmdType]string{ + CmdOmnibus: CmdTextOmnibus, + CmdCalibrate: CmdTextCalibrate, + CmdFlashDownload: CmdTextFlashDownload, + CmdAuxiliary: CmdTextAuxiliary, + CmdExpanded: CmdTextExpanded, +} + +func (obj CmdType) String() string { + val, ok := CmdMap[obj] + if ok { + return val + } + return "Unknown CmdType" +} + +func (obj CmdType) Byte() byte { + return byte(obj) +} diff --git a/consts/cmdAux.go b/consts/cmdAux.go new file mode 100644 index 0000000..220b87d --- /dev/null +++ b/consts/cmdAux.go @@ -0,0 +1,82 @@ +package consts + +/* This file is automatically generated */ + +type CmdAuxType byte + +const ( + CmdAuxSoftwareCRC CmdAuxType = 0x00 + CmdAuxCashBoxTotal CmdAuxType = 0x01 + CmdAuxDeviceResets CmdAuxType = 0x02 + CmdAuxBNFStatus CmdAuxType = 0x10 + CmdAuxSetBezel CmdAuxType = 0x11 + CmdAuxDeviceCapabilities CmdAuxType = 0x0d + CmdAuxCleDeviceResetsarCashBoxTotal CmdAuxType = 0x03 + + CmdAuxAcceptorType CmdAuxType = 0x04 + CmdAuxAcceptorApplicationID CmdAuxType = 0x0e + CmdAuxAcceptorVariantID CmdAuxType = 0x0f + CmdAuxAcceptorSerialNumber CmdAuxType = 0x05 + CmdAuxAcceptorBootPartNumber CmdAuxType = 0x06 + CmdAuxAcceptorApplicationPartNumber CmdAuxType = 0x07 + CmdAuxAcceptorVariantName CmdAuxType = 0x08 + CmdAuxAcceptorVariantPartNumber CmdAuxType = 0x09 + CmdAuxAcceptorAuditLifeTimeTotals CmdAuxType = 0x0a + CmdAuxAcceptorAuditQPMeasures CmdAuxType = 0x0b + CmdAuxAcceptorAuditPerformanceMeasures CmdAuxType = 0x0c +) + +const ( + CmdAuxTextSoftwareCRC = "SoftwareCRC" + CmdAuxTextCashBoxTotal = "CashBoxTotal" + CmdAuxTextDeviceResets = "DeviceResets" + CmdAuxTextBNFStatus = "BNFStatus" + CmdAuxTextSetBezel = "SetBezel" + CmdAuxTextDeviceCapabilities = "DeviceCapabilities" + CmdAuxTextCleDeviceResetsarCashBoxTotal = "CleDeviceResetsarCashBoxTotal" + + CmdAuxTextAcceptorType = "AcceptorType" + CmdAuxTextAcceptorApplicationID = "AcceptorApplicationID" + CmdAuxTextAcceptorVariantID = "AcceptorVariantID" + CmdAuxTextAcceptorSerialNumber = "AcceptorSerialNumber" + CmdAuxTextAcceptorBootPartNumber = "AcceptorBootPartNumber" + CmdAuxTextAcceptorApplicationPartNumber = "AcceptorApplicationPartNumber" + CmdAuxTextAcceptorVariantName = "AcceptorVariantName" + CmdAuxTextAcceptorVariantPartNumber = "AcceptorVariantPartNumber" + CmdAuxTextAcceptorAuditLifeTimeTotals = "AcceptorAuditLifeTimeTotals" + CmdAuxTextAcceptorAuditQPMeasures = "AcceptorAuditQPMeasures" + CmdAuxTextAcceptorAuditPerformanceMeasures = "AcceptorAuditPerformanceMeasures" +) + +var CmdAuxMap = map[CmdAuxType]string{ + CmdAuxSoftwareCRC: CmdAuxTextSoftwareCRC, + CmdAuxCashBoxTotal: CmdAuxTextCashBoxTotal, + CmdAuxDeviceResets: CmdAuxTextDeviceResets, + CmdAuxBNFStatus: CmdAuxTextBNFStatus, + CmdAuxSetBezel: CmdAuxTextSetBezel, + CmdAuxDeviceCapabilities: CmdAuxTextDeviceCapabilities, + CmdAuxCleDeviceResetsarCashBoxTotal: CmdAuxTextCleDeviceResetsarCashBoxTotal, + CmdAuxAcceptorType: CmdAuxTextAcceptorType, + CmdAuxAcceptorApplicationID: CmdAuxTextAcceptorApplicationID, + CmdAuxAcceptorVariantID: CmdAuxTextAcceptorVariantID, + CmdAuxAcceptorSerialNumber: CmdAuxTextAcceptorSerialNumber, + CmdAuxAcceptorBootPartNumber: CmdAuxTextAcceptorBootPartNumber, + CmdAuxAcceptorApplicationPartNumber: CmdAuxTextAcceptorApplicationPartNumber, + CmdAuxAcceptorVariantName: CmdAuxTextAcceptorVariantName, + CmdAuxAcceptorVariantPartNumber: CmdAuxTextAcceptorVariantPartNumber, + CmdAuxAcceptorAuditLifeTimeTotals: CmdAuxTextAcceptorAuditLifeTimeTotals, + CmdAuxAcceptorAuditQPMeasures: CmdAuxTextAcceptorAuditQPMeasures, + CmdAuxAcceptorAuditPerformanceMeasures: CmdAuxTextAcceptorAuditPerformanceMeasures, +} + +func (obj CmdAuxType) String() string { + val, ok := CmdAuxMap[obj] + if ok { + return val + } + return "Unknown CmdAuxType" +} + +func (obj CmdAuxType) Byte() byte { + return byte(obj) +} diff --git a/consts/data.go b/consts/data.go new file mode 100644 index 0000000..f9becf3 --- /dev/null +++ b/consts/data.go @@ -0,0 +1,35 @@ +package consts + +/* This file is automatically generated */ + +type DataType byte + +const ( + DataSTX DataType = 0x02 + DataETX DataType = 0x03 + DataACKMask DataType = 0x06 +) + +const ( + DataTextSTX = "STX" + DataTextETX = "ETX" + DataTextACKMask = "ACKMask" +) + +var DataMap = map[DataType]string{ + DataSTX: DataTextSTX, + DataETX: DataTextETX, + DataACKMask: DataTextACKMask, +} + +func (obj DataType) String() string { + val, ok := DataMap[obj] + if ok { + return val + } + return "Unknown DataType" +} + +func (obj DataType) Byte() byte { + return byte(obj) +} diff --git a/data.go b/data.go index e3d5abb..f2d79f7 100644 --- a/data.go +++ b/data.go @@ -3,26 +3,18 @@ package mpost //////////////////////////////////// type CDataLinkLayer struct { - log *LogGlobalStruct + log *LogStruct Acceptor *CAcceptor - AckToggleBit byte - NakCount uint8 CurrentCommand, EchoDetect []byte PreviousCommand, PreviousReply []byte IdenticalCommandAndReplyCount int } -const ( - STX = 0x02 // Начало текста - ETX = 0x03 // Конец текста - ACKMask = 0x06 // Маска подтверждения -) - -func (a *CAcceptor) NewCDataLinkLayer(lg *LogGlobalStruct) *CDataLinkLayer { +func (a *CAcceptor) NewCDataLinkLayer(lg *LogStruct) *CDataLinkLayer { return &CDataLinkLayer{ Acceptor: a, - log: lg.NewLog("Data"), + log: lg.New("Data"), } } diff --git a/data_escrow.go b/data_escrow.go index 7af41dc..4946e74 100644 --- a/data_escrow.go +++ b/data_escrow.go @@ -1,19 +1,26 @@ package mpost +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/bill" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" +) + //////////////////////////////////// func (dl *CDataLinkLayer) escrowXX(b byte) { - if !dl.Acceptor.connected { - dl.log.Debug().Msg("serial not connected") + if !acceptor.Connected { + dl.log.Msg("serial not connected") return } payload := make([]byte, 4) - dl.Acceptor.ConstructOmnibusCommand(payload, CmdOmnibus, 1) + acceptor.ConstructOmnibusCommand(payload, consts.CmdOmnibus, 1, bill.TypeEnables) payload[2] |= b - dl.Acceptor.messageQueue <- NewCMessage(payload, false) + dl.Acceptor.SendAsynchronousCommand(payload) } func (dl *CDataLinkLayer) EscrowReturn() { @@ -27,101 +34,81 @@ func (dl *CDataLinkLayer) EscrowStack() { //// func (dl *CDataLinkLayer) RaiseEvents() { - if dl.Acceptor.isPoweredUp && dl.Acceptor.shouldRaisePowerUpEvent { - dl.log.Debug().Msg("Power Up Event Raised") - dl.Acceptor.shouldRaisePowerUpEvent = false + if acceptor.IsPoweredUp && acceptor.ShouldRaise.PowerUpEvent { + dl.Acceptor.RaisePowerUpEvent() } - if dl.Acceptor.isVeryFirstPoll { - dl.Acceptor.isVeryFirstPoll = false + if acceptor.IsVeryFirstPoll { + acceptor.IsVeryFirstPoll = false return } - switch dl.Acceptor.deviceState { - case Escrow: - if dl.Acceptor.isPoweredUp && dl.Acceptor.shouldRaisePUPEscrowEvent { - dl.log.Debug().Msg("PUP Escrow Event Raised") - dl.Acceptor.shouldRaisePUPEscrowEvent = false - } else if dl.Acceptor.shouldRaiseEscrowEvent { - dl.log.Debug().Msg("Escrow Event Raised") - dl.Acceptor.shouldRaiseEscrowEvent = false + switch acceptor.Device.State { + case enum.StateEscrow: + if acceptor.IsPoweredUp && acceptor.ShouldRaise.PUPEscrowEvent { + dl.Acceptor.RaisePUPEscrowEvent() + } else if acceptor.ShouldRaise.EscrowEvent { + dl.Acceptor.RaiseEscrowEvent() } - case Stacked: - if dl.Acceptor.shouldRaiseStackedEvent { - dl.log.Debug().Msg("Stacked Event Raised") - dl.Acceptor.shouldRaiseStackedEvent = false + case enum.StateStacked: + if acceptor.ShouldRaise.StackedEvent { + dl.Acceptor.RaiseStackedEvent() } - case Returned: - if dl.Acceptor.shouldRaiseReturnedEvent { - dl.log.Debug().Msg("Returned Event Raised") - dl.Acceptor.shouldRaiseReturnedEvent = false + case enum.StateReturned: + if acceptor.ShouldRaise.ReturnedEvent { + dl.Acceptor.RaiseReturnedEvent() } - case Rejected: - if dl.Acceptor.shouldRaiseRejectedEvent { - dl.log.Debug().Msg("Rejected Event Raised") - dl.Acceptor.shouldRaiseRejectedEvent = false + case enum.StateRejected: + if acceptor.ShouldRaise.RejectedEvent { + dl.Acceptor.RaiseRejectedEvent() } - case Stalled: - if dl.Acceptor.shouldRaiseStallDetectedEvent { - dl.log.Debug().Msg("Stall Detected Event Raised") - dl.Acceptor.shouldRaiseStallDetectedEvent = false + case enum.StateStalled: + if acceptor.ShouldRaise.StallDetectedEvent { + dl.Acceptor.RaiseStallDetectedEvent() } } - if dl.Acceptor.deviceState != Stalled && dl.Acceptor.shouldRaiseStallClearedEvent { - dl.log.Debug().Msg("Stall Cleared Event Raised") - dl.Acceptor.shouldRaiseStallClearedEvent = false + if acceptor.Device.State != enum.StateStalled && acceptor.ShouldRaise.StallClearedEvent { + dl.Acceptor.RaiseStallClearedEvent() } - if dl.Acceptor.cashBoxFull && dl.Acceptor.shouldRaiseStackerFullEvent { - dl.log.Debug().Msg("Stacker Full Event Raised") - dl.Acceptor.shouldRaiseStackerFullEvent = false + if acceptor.Cash.BoxFull && acceptor.ShouldRaise.StackerFullEvent { + dl.Acceptor.RaiseStackerFullEvent() } - if dl.Acceptor.isCheated && dl.Acceptor.shouldRaiseCheatedEvent { - dl.log.Debug().Msg("Cheated Event Raised") - dl.Acceptor.shouldRaiseCheatedEvent = false + if acceptor.IsCheated && acceptor.ShouldRaise.CheatedEvent { + dl.Acceptor.RaiseCheatedEvent() } - if dl.Acceptor.cashBoxAttached && dl.Acceptor.shouldRaiseCashBoxAttachedEvent { - dl.log.Debug().Msg("Cash Box Attached Event Raised") - dl.Acceptor.shouldRaiseCashBoxAttachedEvent = false - dl.Acceptor.shouldRaiseCashBoxRemovedEvent = true + if acceptor.Cash.BoxAttached && acceptor.ShouldRaise.CashBoxAttachedEvent { + dl.Acceptor.RaiseCashBoxAttachedEvent() } - if !dl.Acceptor.cashBoxAttached && dl.Acceptor.shouldRaiseCashBoxRemovedEvent { - dl.log.Debug().Msg("Cash Box Removed Event Raised") - dl.Acceptor.shouldRaiseCashBoxRemovedEvent = false - dl.Acceptor.shouldRaiseCashBoxAttachedEvent = true + if !acceptor.Cash.BoxAttached && acceptor.ShouldRaise.CashBoxRemovedEvent { + dl.Acceptor.RaiseCashBoxRemovedEvent() } - if dl.Acceptor.devicePaused && dl.Acceptor.shouldRaisePauseDetectedEvent { - dl.log.Debug().Msg("Pause Detected Event Raised") - dl.Acceptor.shouldRaisePauseDetectedEvent = false + if acceptor.Device.Paused && acceptor.ShouldRaise.PauseDetectedEvent { + dl.Acceptor.RaisePauseDetectedEvent() } - if !dl.Acceptor.devicePaused && dl.Acceptor.shouldRaisePauseClearedEvent { - dl.log.Debug().Msg("Pause Cleared Event Raised") - dl.Acceptor.shouldRaisePauseClearedEvent = false + if !acceptor.Device.Paused && acceptor.ShouldRaise.PauseClearedEvent { + dl.Acceptor.RaisePauseClearedEvent() } - if dl.Acceptor.isDeviceJammed && dl.Acceptor.shouldRaiseJamDetectedEvent { - dl.log.Debug().Msg("Jam Detected Event Raised") - dl.Acceptor.shouldRaiseJamDetectedEvent = false + if acceptor.Device.Jammed && acceptor.ShouldRaise.JamDetectedEvent { + dl.Acceptor.RaiseJamDetectedEvent() } - if !dl.Acceptor.isDeviceJammed && dl.Acceptor.shouldRaiseJamClearedEvent { - dl.log.Debug().Msg("Jam Cleared Event Raised") - dl.Acceptor.shouldRaiseJamClearedEvent = false + if !acceptor.Device.Jammed && acceptor.ShouldRaise.JamClearedEvent { + dl.Acceptor.RaiseJamClearedEvent() } - if dl.Acceptor.isInvalidCommand && dl.Acceptor.shouldRaiseInvalidCommandEvent { - dl.log.Debug().Msg("Invalid Command Event Raised") - dl.Acceptor.shouldRaiseInvalidCommandEvent = false + if acceptor.IsInvalidCommand && acceptor.ShouldRaise.InvalidCommandEvent { + dl.Acceptor.RaiseInvalidCommandEvent() } - if dl.Acceptor.shouldRaiseCalibrateFinishEvent { - dl.log.Debug().Msg("Calibrate Finish Event Raised") - dl.Acceptor.shouldRaiseCalibrateFinishEvent = false + if acceptor.ShouldRaise.CalibrateFinishEvent { + dl.Acceptor.RaiseCalibrateFinishEvent() } } diff --git a/data_log.go b/data_log.go deleted file mode 100644 index 534423f..0000000 --- a/data_log.go +++ /dev/null @@ -1,13 +0,0 @@ -package mpost - -//////////////////////////////////// - -func (dl *CDataLinkLayer) LogCommandAndReply(command, reply []byte, wasEchoDiscarded bool) { - dl.log.Debug().Bytes("command", command).Bytes("reply", reply).Bool("wasEchoDiscarded", wasEchoDiscarded).Bool("wasEchoDiscarded", wasEchoDiscarded).Send() -} - -func (dl *CDataLinkLayer) FlushIdenticalTransactionsToLog() { - if dl.IdenticalCommandAndReplyCount > 0 { - dl.log.Debug().Int("IdenticalCommandAndReplyCount", dl.IdenticalCommandAndReplyCount).Send() - } -} diff --git a/data_process.go b/data_process.go index 092ac0a..0e114d8 100644 --- a/data_process.go +++ b/data_process.go @@ -1,5 +1,10 @@ package mpost +import ( + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/enum" +) + //////////////////////////////////// func (dl *CDataLinkLayer) ProcessStandardOmnibusReply(reply []byte) { @@ -29,16 +34,16 @@ func (dl *CDataLinkLayer) ProcessExtendedOmnibusBarCodeReply(reply []byte) { dl.Acceptor.processData3(reply[7]) dl.Acceptor.processData5(reply[9]) - if dl.Acceptor.deviceState == Escrow { - dl.Acceptor.barCode = "" + if acceptor.Device.State == enum.StateEscrow { + acceptor.BarCode = "" for i := 10; i < 38; i++ { if reply[i] != '(' { - dl.Acceptor.barCode += string(reply[i]) + acceptor.BarCode += string(reply[i]) } else { break } } - dl.Acceptor.docType = Barcode + dl.Acceptor.docType = enum.DocumentBarcode } } @@ -67,7 +72,7 @@ func (dl *CDataLinkLayer) ProcessExtendedOmnibusExpandedCouponReply(reply []byte dl.Acceptor.processData3(reply[7]) dl.Acceptor.processData5(reply[9]) - if dl.Acceptor.deviceState == Escrow || (dl.Acceptor.deviceState == Stacked && !dl.Acceptor.wasDocTypeSetOnEscrow) { + if acceptor.Device.State == enum.StateEscrow || (acceptor.Device.State == enum.StateStacked && !acceptor.WasDocTypeSetOnEscrow) { couponData := ((int(reply[10]) & 0x0F) << 12) + ((int(reply[11]) & 0x0F) << 8) + ((int(reply[12]) & 0x0F) << 4) + @@ -82,7 +87,7 @@ func (dl *CDataLinkLayer) ProcessExtendedOmnibusExpandedCouponReply(reply []byte dl.Acceptor.coupon = NewCCoupon(ownerID, value) - dl.Acceptor.docType = Coupon - dl.Acceptor.wasDocTypeSetOnEscrow = dl.Acceptor.deviceState == Escrow + dl.Acceptor.docType = enum.DocumentCoupon + acceptor.WasDocTypeSetOnEscrow = acceptor.Device.State == enum.StateEscrow } } diff --git a/data_send_replay.go b/data_send_replay.go index 71fb028..3dd3c38 100644 --- a/data_send_replay.go +++ b/data_send_replay.go @@ -3,49 +3,28 @@ package mpost import ( "bufio" "errors" + "github.com/hard-soft-ware/mpost/acceptor" + "github.com/hard-soft-ware/mpost/command" + "github.com/hard-soft-ware/mpost/consts" + "github.com/hard-soft-ware/mpost/enum" "io" - "time" ) //////////////////////////////////// func (dl *CDataLinkLayer) SendPacket(payload []byte) { - payloadLength := len(payload) - commandLength := payloadLength + 4 // STX + Length char + ETX + Checksum + send := command.Create(payload) - command := make([]byte, 0, commandLength) - command = append(command, STX) - command = append(command, byte(commandLength)) + dl.CurrentCommand = send + dl.EchoDetect = send - command = append(command, payload...) - command[2] |= dl.AckToggleBit - - command = append(command, ETX) - command = append(command, dl.ComputeCheckSum(command)) - - dl.CurrentCommand = command - dl.EchoDetect = command - - dl.log.Debug().Str("data", byteSliceToString(command)).Msg("SERIAL SEND") - n, err := dl.Acceptor.port.Write(command) + dl.log.Bytes("SERIAL SEND >>> ", send) + n, err := dl.Acceptor.port.Write(send) if err != nil || n == 0 { - dl.log.Debug().Err(err).Msg("Failed to write to port") - - dl.Acceptor.port.Close() - dl.Acceptor.OpenPort(dl.log) - } -} - -func (dl *CDataLinkLayer) WaitForQuiet() { - for { - buf := make([]byte, 1) - timeout := 20 * time.Millisecond - - dl.Acceptor.port.SetReadTimeout(timeout) - - _, err := dl.Acceptor.port.Read(buf) + dl.log.Err("Failed to write to port", err) + err = dl.Acceptor.port.Restart() if err != nil { - return + dl.log.Err("Failed restart to port", err) } } } @@ -55,14 +34,14 @@ func (dl *CDataLinkLayer) WaitForQuiet() { func (dl *CDataLinkLayer) ReceiveReply() ([]byte, error) { reply := []byte{} - timeout := dl.Acceptor.transactionTimeout - if dl.Acceptor.deviceState == DownloadStart || dl.Acceptor.deviceState == Downloading { - timeout = dl.Acceptor.downloadTimeout + timeout := acceptor.Timeout.Transaction + if acceptor.Device.State == enum.StateDownloadStart || acceptor.Device.State == enum.StateDownloading { + timeout = acceptor.Timeout.Download } - dl.Acceptor.port.SetReadTimeout(timeout) + dl.Acceptor.port.SetTimeout(timeout) - reader := bufio.NewReader(dl.Acceptor.port) + reader := bufio.NewReader(dl.Acceptor.port.Port()) stxAndLength := make([]byte, 2) bytesRead, err := io.ReadFull(reader, stxAndLength) if err != nil { @@ -71,7 +50,7 @@ func (dl *CDataLinkLayer) ReceiveReply() ([]byte, error) { if bytesRead < 2 { return nil, errors.New("received insufficient bytes") } - if stxAndLength[0] != STX { + if stxAndLength[0] != consts.DataSTX.Byte() { return nil, errors.New("invalid STX received") } @@ -99,36 +78,12 @@ func (dl *CDataLinkLayer) ReceiveReply() ([]byte, error) { } } - dl.log.Debug().Str("data", byteSliceToString(reply)).Msg("SERIAL READ") + dl.log.Bytes("SERIAL READ <<< ", reply) return reply, nil } //// -func (dl *CDataLinkLayer) ReplyAcked(reply []byte) bool { - if len(reply) < 3 { - return false - } - - if (reply[2] & ACKMask) == dl.AckToggleBit { - dl.AckToggleBit ^= 0x01 // Переключаем бит подтверждения - - dl.NakCount = 0 - - return true - } else { - dl.NakCount++ - - // Если получено 8 последовательных NAK, принудительно переключаем бит - if dl.NakCount == 8 { - dl.AckToggleBit ^= 0x01 - dl.NakCount = 0 - } - - return false - } -} - func (dl *CDataLinkLayer) ProcessReply(reply []byte) { if len(reply) < 3 { return @@ -141,7 +96,7 @@ func (dl *CDataLinkLayer) ProcessReply(reply []byte) { } if (ctl & 0x70) == 0x50 { - dl.Acceptor.deviceState = DownloadRestart + acceptor.Device.State = enum.StateDownloadRestart } if (ctl & 0x70) == 0x70 { @@ -151,18 +106,18 @@ func (dl *CDataLinkLayer) ProcessReply(reply []byte) { dl.ProcessExtendedOmnibusBarCodeReply(reply) case 0x02: dl.ProcessExtendedOmnibusExpandedNoteReply(reply) - if dl.Acceptor.deviceState == Escrow || (dl.Acceptor.deviceState == Stacked && !dl.Acceptor.wasDocTypeSetOnEscrow) { - if dl.Acceptor.capOrientationExt { - switch dl.Acceptor.orientationCtlExt { - case OneWay: - if dl.Acceptor.escrowOrientation != RightUp { + if acceptor.Device.State == enum.StateEscrow || (acceptor.Device.State == enum.StateStacked && !acceptor.WasDocTypeSetOnEscrow) { + if acceptor.Cap.OrientationExt { + switch acceptor.OrientationCtlExt { + case enum.OrientationControlOneWay: + if acceptor.EscrowOrientation != enum.OrientationRightUp { dl.EscrowReturn() } - case TwoWay: - if dl.Acceptor.escrowOrientation != RightUp && dl.Acceptor.escrowOrientation != LeftUp { + case enum.OrientationControlTwoWay: + if acceptor.EscrowOrientation != enum.OrientationRightUp && acceptor.EscrowOrientation != enum.OrientationLeftUp { dl.EscrowReturn() } - case FourWay: + case enum.OrientationControlFourWay: // Accept all orientations. } } @@ -173,12 +128,12 @@ func (dl *CDataLinkLayer) ProcessReply(reply []byte) { dl.RaiseEvents() } - if dl.Acceptor.deviceState == Escrow && dl.Acceptor.autoStack { + if acceptor.Device.State == enum.StateEscrow && acceptor.AutoStack { dl.EscrowStack() - dl.Acceptor.shouldRaiseEscrowEvent = false + acceptor.ShouldRaise.EscrowEvent = false } - if dl.Acceptor.deviceState != Escrow && dl.Acceptor.deviceState != Stacking { - dl.Acceptor.wasDocTypeSetOnEscrow = false + if acceptor.Device.State != enum.StateEscrow && acceptor.Device.State != enum.StateStacking { + acceptor.WasDocTypeSetOnEscrow = false } } diff --git a/enum.go b/enum.go deleted file mode 100644 index ed28fad..0000000 --- a/enum.go +++ /dev/null @@ -1,144 +0,0 @@ -package mpost - -//////////////////////////////////// - -type BNFStatus byte - -const ( - Unknown BNFStatus = iota - OK - NotAttached - Error -) - -//// - -type DocumentType byte - -const ( - None DocumentType = iota - NoValue - Bill - Barcode - Coupon -) - -//// - -type Orientation byte - -const ( - RightUp Orientation = iota - RightDown - LeftUp - LeftDown - UnknownOrientation -) - -//// - -type OrientationControl byte - -const ( - FourWay OrientationControl = iota - TwoWay - OneWay -) - -//// - -type PowerUp byte - -const ( - A PowerUp = iota - B - C - E -) - -//// - -type PupExt byte - -const ( - Return PupExt = iota - OutOfService - StackNoCredit - Stack - WaitNoCredit - Wait -) - -//// - -type State byte - -const ( - Escrow State = iota - Stacked - Returned - Rejected - Stalled - Accepting - CalibrateStart - Calibrating - Connecting - Disconnected - Downloading - DownloadRestart - DownloadStart - Failed - Idling - PupEscrow - Returning - Stacking -) - -//// - -type Bezel byte - -const ( - Standard Bezel = iota - Platform - Diagnostic -) - -//// - -type Event byte - -type EventHandler func(*CAcceptor, int) - -const ( - Events_Begin Event = iota - ConnectedEvent - EscrowEvent - PUPEscrowEvent - StackedEvent - ReturnedEvent - RejectedEvent - CheatedEvent - StackerFullEvent - CalibrateStartEvent - CalibrateProgressEvent - CalibrateFinishEvent - DownloadStartEvent - DownloadRestartEvent - DownloadProgressEvent - DownloadFinishEvent - PauseDetectedEvent - PauseClearedEvent - StallDetectedEvent - StallClearedEvent - JamDetectedEvent - JamClearedEvent - PowerUpEvent - InvalidCommandEvent - CashBoxAttachedEvent - CashBoxRemovedEvent - DisconnectedEvent - Events_End -) - -//// diff --git a/enum/bezel.go b/enum/bezel.go new file mode 100644 index 0000000..b08ca1f --- /dev/null +++ b/enum/bezel.go @@ -0,0 +1,31 @@ +package enum + +/* This file is automatically generated */ + +type BezelType byte + +const ( + BezelStandard BezelType = 0 + BezelPlatform BezelType = 1 + BezelDiagnostic BezelType = 2 +) + +const ( + BezelTextStandard = "Standard" + BezelTextPlatform = "Platform" + BezelTextDiagnostic = "Diagnostic" +) + +var BezelMap = map[BezelType]string{ + BezelStandard: BezelTextStandard, + BezelPlatform: BezelTextPlatform, + BezelDiagnostic: BezelTextDiagnostic, +} + +func (obj BezelType) String() string { + val, ok := BezelMap[obj] + if ok { + return val + } + return "Unknown BezelType" +} diff --git a/enum/document.go b/enum/document.go new file mode 100644 index 0000000..60b08c7 --- /dev/null +++ b/enum/document.go @@ -0,0 +1,37 @@ +package enum + +/* This file is automatically generated */ + +type DocumentType byte + +const ( + DocumentNone DocumentType = 0 + DocumentNoValue DocumentType = 1 + DocumentBill DocumentType = 2 + DocumentBarcode DocumentType = 3 + DocumentCoupon DocumentType = 4 +) + +const ( + DocumentTextNone = "None" + DocumentTextNoValue = "NoValue" + DocumentTextBill = "Bill" + DocumentTextBarcode = "Barcode" + DocumentTextCoupon = "Coupon" +) + +var DocumentMap = map[DocumentType]string{ + DocumentNone: DocumentTextNone, + DocumentNoValue: DocumentTextNoValue, + DocumentBill: DocumentTextBill, + DocumentBarcode: DocumentTextBarcode, + DocumentCoupon: DocumentTextCoupon, +} + +func (obj DocumentType) String() string { + val, ok := DocumentMap[obj] + if ok { + return val + } + return "Unknown DocumentType" +} diff --git a/enum/event.go b/enum/event.go new file mode 100644 index 0000000..a2f919b --- /dev/null +++ b/enum/event.go @@ -0,0 +1,106 @@ +package enum + +/* This file is automatically generated */ + +type EventType byte + +const ( + Event_Begin EventType = 0 + EventConnected EventType = 1 + EventEscrow EventType = 2 + EventPUPEscrow EventType = 3 + EventStacked EventType = 4 + EventReturned EventType = 5 + EventRejected EventType = 6 + EventCheated EventType = 7 + EventStackerFull EventType = 8 + EventCalibrateStart EventType = 9 + EventCalibrateProgress EventType = 10 + EventCalibrateFinish EventType = 11 + EventDownloadStart EventType = 12 + EventDownloadRestart EventType = 13 + EventDownloadProgress EventType = 14 + EventDownloadFinish EventType = 15 + EventPauseDetected EventType = 16 + EventPauseCleared EventType = 17 + EventStallDetected EventType = 18 + EventStallCleared EventType = 19 + EventJamDetected EventType = 20 + EventJamCleared EventType = 21 + EventPowerUp EventType = 22 + EventInvalidCommand EventType = 23 + EventCashBoxAttached EventType = 24 + EventCashBoxRemoved EventType = 25 + EventDisconnected EventType = 26 + Event_End EventType = 27 +) + +const ( + EventText_Begin = "_Begin" + EventTextConnected = "Connected" + EventTextEscrow = "Escrow" + EventTextPUPEscrow = "PUPEscrow" + EventTextStacked = "Stacked" + EventTextReturned = "Returned" + EventTextRejected = "Rejected" + EventTextCheated = "Cheated" + EventTextStackerFull = "StackerFull" + EventTextCalibrateStart = "CalibrateStart" + EventTextCalibrateProgress = "CalibrateProgress" + EventTextCalibrateFinish = "CalibrateFinish" + EventTextDownloadStart = "DownloadStart" + EventTextDownloadRestart = "DownloadRestart" + EventTextDownloadProgress = "DownloadProgress" + EventTextDownloadFinish = "DownloadFinish" + EventTextPauseDetected = "PauseDetected" + EventTextPauseCleared = "PauseCleared" + EventTextStallDetected = "StallDetected" + EventTextStallCleared = "StallCleared" + EventTextJamDetected = "JamDetected" + EventTextJamCleared = "JamCleared" + EventTextPowerUp = "PowerUp" + EventTextInvalidCommand = "InvalidCommand" + EventTextCashBoxAttached = "CashBoxAttached" + EventTextCashBoxRemoved = "CashBoxRemoved" + EventTextDisconnected = "Disconnected" + EventText_End = "_End" +) + +var EventMap = map[EventType]string{ + Event_Begin: EventText_Begin, + EventConnected: EventTextConnected, + EventEscrow: EventTextEscrow, + EventPUPEscrow: EventTextPUPEscrow, + EventStacked: EventTextStacked, + EventReturned: EventTextReturned, + EventRejected: EventTextRejected, + EventCheated: EventTextCheated, + EventStackerFull: EventTextStackerFull, + EventCalibrateStart: EventTextCalibrateStart, + EventCalibrateProgress: EventTextCalibrateProgress, + EventCalibrateFinish: EventTextCalibrateFinish, + EventDownloadStart: EventTextDownloadStart, + EventDownloadRestart: EventTextDownloadRestart, + EventDownloadProgress: EventTextDownloadProgress, + EventDownloadFinish: EventTextDownloadFinish, + EventPauseDetected: EventTextPauseDetected, + EventPauseCleared: EventTextPauseCleared, + EventStallDetected: EventTextStallDetected, + EventStallCleared: EventTextStallCleared, + EventJamDetected: EventTextJamDetected, + EventJamCleared: EventTextJamCleared, + EventPowerUp: EventTextPowerUp, + EventInvalidCommand: EventTextInvalidCommand, + EventCashBoxAttached: EventTextCashBoxAttached, + EventCashBoxRemoved: EventTextCashBoxRemoved, + EventDisconnected: EventTextDisconnected, + Event_End: EventText_End, +} + +func (obj EventType) String() string { + val, ok := EventMap[obj] + if ok { + return val + } + return "Unknown EventType" +} diff --git a/enum/orientation.go b/enum/orientation.go new file mode 100644 index 0000000..0b37af4 --- /dev/null +++ b/enum/orientation.go @@ -0,0 +1,37 @@ +package enum + +/* This file is automatically generated */ + +type OrientationType byte + +const ( + OrientationRightUp OrientationType = 0 + OrientationRightDown OrientationType = 1 + OrientationLeftUp OrientationType = 2 + OrientationLeftDown OrientationType = 3 + OrientationUnknownOrientation OrientationType = 4 +) + +const ( + OrientationTextRightUp = "RightUp" + OrientationTextRightDown = "RightDown" + OrientationTextLeftUp = "LeftUp" + OrientationTextLeftDown = "LeftDown" + OrientationTextUnknownOrientation = "UnknownOrientation" +) + +var OrientationMap = map[OrientationType]string{ + OrientationRightUp: OrientationTextRightUp, + OrientationRightDown: OrientationTextRightDown, + OrientationLeftUp: OrientationTextLeftUp, + OrientationLeftDown: OrientationTextLeftDown, + OrientationUnknownOrientation: OrientationTextUnknownOrientation, +} + +func (obj OrientationType) String() string { + val, ok := OrientationMap[obj] + if ok { + return val + } + return "Unknown OrientationType" +} diff --git a/enum/orientation_control.go b/enum/orientation_control.go new file mode 100644 index 0000000..280170a --- /dev/null +++ b/enum/orientation_control.go @@ -0,0 +1,31 @@ +package enum + +/* This file is automatically generated */ + +type OrientationControlType byte + +const ( + OrientationControlFourWay OrientationControlType = 0 + OrientationControlTwoWay OrientationControlType = 1 + OrientationControlOneWay OrientationControlType = 2 +) + +const ( + OrientationControlTextFourWay = "FourWay" + OrientationControlTextTwoWay = "TwoWay" + OrientationControlTextOneWay = "OneWay" +) + +var OrientationControlMap = map[OrientationControlType]string{ + OrientationControlFourWay: OrientationControlTextFourWay, + OrientationControlTwoWay: OrientationControlTextTwoWay, + OrientationControlOneWay: OrientationControlTextOneWay, +} + +func (obj OrientationControlType) String() string { + val, ok := OrientationControlMap[obj] + if ok { + return val + } + return "Unknown OrientationControlType" +} diff --git a/enum/powerUp.go b/enum/powerUp.go new file mode 100644 index 0000000..eca4a03 --- /dev/null +++ b/enum/powerUp.go @@ -0,0 +1,34 @@ +package enum + +/* This file is automatically generated */ + +type PowerUpType byte + +const ( + PowerUpA PowerUpType = 0 + PowerUpB PowerUpType = 1 + PowerUpC PowerUpType = 2 + PowerUpE PowerUpType = 3 +) + +const ( + PowerUpTextA = "A" + PowerUpTextB = "B" + PowerUpTextC = "C" + PowerUpTextE = "E" +) + +var PowerUpMap = map[PowerUpType]string{ + PowerUpA: PowerUpTextA, + PowerUpB: PowerUpTextB, + PowerUpC: PowerUpTextC, + PowerUpE: PowerUpTextE, +} + +func (obj PowerUpType) String() string { + val, ok := PowerUpMap[obj] + if ok { + return val + } + return "Unknown PowerUpType" +} diff --git a/enum/pupExt.go b/enum/pupExt.go new file mode 100644 index 0000000..9c5e291 --- /dev/null +++ b/enum/pupExt.go @@ -0,0 +1,40 @@ +package enum + +/* This file is automatically generated */ + +type PupExtType byte + +const ( + PupExtReturn PupExtType = 0 + PupExtOutOfService PupExtType = 1 + PupExtStack PupExtType = 2 + PupExtStackNoCredit PupExtType = 3 + PupExtWait PupExtType = 4 + PupExtWaitNoCredit PupExtType = 5 +) + +const ( + PupExtTextReturn = "Return" + PupExtTextOutOfService = "OutOfService" + PupExtTextStack = "Stack" + PupExtTextStackNoCredit = "StackNoCredit" + PupExtTextWait = "Wait" + PupExtTextWaitNoCredit = "WaitNoCredit" +) + +var PupExtMap = map[PupExtType]string{ + PupExtReturn: PupExtTextReturn, + PupExtOutOfService: PupExtTextOutOfService, + PupExtStack: PupExtTextStack, + PupExtStackNoCredit: PupExtTextStackNoCredit, + PupExtWait: PupExtTextWait, + PupExtWaitNoCredit: PupExtTextWaitNoCredit, +} + +func (obj PupExtType) String() string { + val, ok := PupExtMap[obj] + if ok { + return val + } + return "Unknown PupExtType" +} diff --git a/enum/state.go b/enum/state.go new file mode 100644 index 0000000..a672133 --- /dev/null +++ b/enum/state.go @@ -0,0 +1,76 @@ +package enum + +/* This file is automatically generated */ + +type StateType byte + +const ( + StateEscrow StateType = 0 + StateStacked StateType = 1 + StateReturned StateType = 2 + StateRejected StateType = 3 + StateStalled StateType = 4 + StateAccepting StateType = 5 + StateCalibrateStart StateType = 6 + StateCalibrating StateType = 7 + StateConnecting StateType = 8 + StateDisconnected StateType = 9 + StateDownloading StateType = 10 + StateDownloadRestart StateType = 11 + StateDownloadStart StateType = 12 + StateFailed StateType = 13 + StateIdling StateType = 14 + StatePupEscrow StateType = 15 + StateReturning StateType = 16 + StateStacking StateType = 17 +) + +const ( + StateTextEscrow = "Escrow" + StateTextStacked = "Stacked" + StateTextReturned = "Returned" + StateTextRejected = "Rejected" + StateTextStalled = "Stalled" + StateTextAccepting = "Accepting" + StateTextCalibrateStart = "CalibrateStart" + StateTextCalibrating = "Calibrating" + StateTextConnecting = "Connecting" + StateTextDisconnected = "Disconnected" + StateTextDownloading = "Downloading" + StateTextDownloadRestart = "DownloadRestart" + StateTextDownloadStart = "DownloadStart" + StateTextFailed = "Failed" + StateTextIdling = "Idling" + StateTextPupEscrow = "PupEscrow" + StateTextReturning = "Returning" + StateTextStacking = "Stacking" +) + +var StateMap = map[StateType]string{ + StateEscrow: StateTextEscrow, + StateStacked: StateTextStacked, + StateReturned: StateTextReturned, + StateRejected: StateTextRejected, + StateStalled: StateTextStalled, + StateAccepting: StateTextAccepting, + StateCalibrateStart: StateTextCalibrateStart, + StateCalibrating: StateTextCalibrating, + StateConnecting: StateTextConnecting, + StateDisconnected: StateTextDisconnected, + StateDownloading: StateTextDownloading, + StateDownloadRestart: StateTextDownloadRestart, + StateDownloadStart: StateTextDownloadStart, + StateFailed: StateTextFailed, + StateIdling: StateTextIdling, + StatePupEscrow: StateTextPupEscrow, + StateReturning: StateTextReturning, + StateStacking: StateTextStacking, +} + +func (obj StateType) String() string { + val, ok := StateMap[obj] + if ok { + return val + } + return "Unknown StateType" +} diff --git a/enum/status.go b/enum/status.go new file mode 100644 index 0000000..3e0c4cf --- /dev/null +++ b/enum/status.go @@ -0,0 +1,34 @@ +package enum + +/* This file is automatically generated */ + +type BNFStatusType byte + +const ( + BNFStatusUnknown BNFStatusType = 0 + BNFStatusError BNFStatusType = 1 + BNFStatusOK BNFStatusType = 2 + BNFStatusNotAttached BNFStatusType = 3 +) + +const ( + BNFStatusTextUnknown = "Unknown" + BNFStatusTextError = "Error" + BNFStatusTextOK = "OK" + BNFStatusTextNotAttached = "NotAttached" +) + +var BNFStatusMap = map[BNFStatusType]string{ + BNFStatusUnknown: BNFStatusTextUnknown, + BNFStatusError: BNFStatusTextError, + BNFStatusOK: BNFStatusTextOK, + BNFStatusNotAttached: BNFStatusTextNotAttached, +} + +func (obj BNFStatusType) String() string { + val, ok := BNFStatusMap[obj] + if ok { + return val + } + return "Unknown BNFStatusType" +} diff --git a/log.go b/log.go index 374896b..a58477a 100644 --- a/log.go +++ b/log.go @@ -1,63 +1,71 @@ package mpost import ( + "fmt" + "github.com/hard-soft-ware/mpost/enum" "github.com/rs/zerolog" + "strings" ) ///////////////////////////////////////////////////////// -const TextLogMsgInit = "INIT" +type LogStruct struct { + isEnable bool + log zerolog.Logger + index string + printBytes bool +} -type LogGlobalStruct struct { - log zerolog.Logger - index string +func NewLog(log zerolog.Logger, root string, printBytes bool) LogStruct { + obj := LogStruct{ + isEnable: true, + index: root, + log: log, + printBytes: printBytes, + } + return obj } -//// +func (obj *LogStruct) New(point string) *LogStruct { + newObj := *obj + newObj.index = obj.index + "/" + point + return &newObj +} -func NewLog(log zerolog.Logger, root string) *LogGlobalStruct { - obj := LogGlobalStruct{index: root} +//// - newLogger := log.With().Str("index", obj.index).Logger() - obj.log = newLogger - newLogger.Debug().Msg(TextLogMsgInit) +func (obj *LogStruct) Msg(msg string) { + if !obj.isEnable { + return + } + obj.log.Debug().Str("index", obj.index).Msg(msg) +} - return &obj +func (obj *LogStruct) Err(msg string, err error) { + if !obj.isEnable { + return + } + obj.log.Debug().Str("index", obj.index).Err(err).Msg(msg) } -func (obj *LogGlobalStruct) NewLog(point string) *LogGlobalStruct { - newObj := LogGlobalStruct{index: obj.index + "/" + point} +func (obj *LogStruct) Bytes(msg string, data []byte) { + if !obj.isEnable || !obj.printBytes { + return + } - newLogger := obj.log.With().Str("index", newObj.index).Logger() - newObj.log = newLogger - newLogger.Debug().Msg(TextLogMsgInit) + var sb strings.Builder + for i, byteVal := range data { + if i > 0 { + sb.WriteString(" ") + } + fmt.Fprintf(&sb, "%02X", byteVal) + } - return &newObj + obj.log.Debug().Str("index", obj.index).Str("data", sb.String()).Int("len", len(data)).Msg(msg) } -//// +// -func (obj *LogGlobalStruct) Debug() *zerolog.Event { - return obj.log.Debug() -} -func (obj *LogGlobalStruct) Info() *zerolog.Event { - return obj.log.Info() -} -func (obj *LogGlobalStruct) Log() *zerolog.Event { - return obj.log.Log() -} -func (obj *LogGlobalStruct) Error() *zerolog.Event { - return obj.log.Error() -} -func (obj *LogGlobalStruct) Panic() *zerolog.Event { - return obj.log.Panic() -} -func (obj *LogGlobalStruct) Fatal() *zerolog.Event { - return obj.log.Fatal() -} -func (obj *LogGlobalStruct) Warn() *zerolog.Event { - return obj.log.Warn() -} -func (obj *LogGlobalStruct) Trace() *zerolog.Event { - return obj.log.Trace() +func (obj *LogStruct) Event(event enum.EventType) { + obj.log.Debug().Str("index", obj.index).Str("event", event.String()).Msg("Event") } diff --git a/serial/errors.go b/serial/errors.go new file mode 100644 index 0000000..b349af2 --- /dev/null +++ b/serial/errors.go @@ -0,0 +1,13 @@ +package serial + +import "errors" + +//////////////////////////////////// + +var ( + ErrNotConnect = errors.New("not connect") + ErrFailSendCommand = errors.New("fail send command") + + ErrReceivedInsufficientBytes = errors.New("received insufficient bytes") + ErrInvalidSTX = errors.New("invalid STX") +) diff --git a/serial/func.go b/serial/func.go new file mode 100644 index 0000000..a8bd33f --- /dev/null +++ b/serial/func.go @@ -0,0 +1,26 @@ +package serial + +import ( + "go.bug.st/serial" + "time" +) + +//////////////////////////////////// + +func (obj *SerialStruct) Port() serial.Port { + return obj.port +} + +func (obj *SerialStruct) Write(p []byte) (n int, err error) { + if !*obj.connect { + return 0, ErrNotConnect + } + return obj.port.Write(p) +} + +func (obj *SerialStruct) SetTimeout(t time.Duration) error { + if !*obj.connect { + return ErrNotConnect + } + return obj.port.SetReadTimeout(t) +} diff --git a/serial/serial.go b/serial/serial.go new file mode 100644 index 0000000..fff64e4 --- /dev/null +++ b/serial/serial.go @@ -0,0 +1,82 @@ +package serial + +import ( + "go.bug.st/serial" + "time" +) + +//////////////////////////////////// + +type SerialStruct struct { + PortName string + connect *bool + + port serial.Port + mode *serial.Mode + + posCoin bool +} + +func Open(portName string, connectStatus *bool) (*SerialStruct, error) { + obj := SerialStruct{} + obj.PortName = portName + obj.connect = connectStatus + + obj.mode = &serial.Mode{ + BaudRate: 9600, + DataBits: 7, + Parity: serial.EvenParity, + StopBits: serial.OneStopBit, + } + + err := obj.open() + if err != nil { + return nil, err + } + + return &obj, nil +} + +func (obj *SerialStruct) open() error { + port, err := serial.Open(obj.PortName, obj.mode) + if err != nil { + return err + } + + port.SetReadTimeout(100 * time.Millisecond) + port.ResetInputBuffer() + + port.SetDTR(false) + port.SetRTS(true) + time.Sleep(100 * time.Millisecond) + + port.SetDTR(true) + port.SetRTS(false) + time.Sleep(5 * time.Millisecond) + + port.GetModemStatusBits() + + port.ResetInputBuffer() + obj.port = port + *obj.connect = true + return nil +} + +func (obj *SerialStruct) Restart() error { + if *obj.connect { + obj.port.Close() + time.Sleep(100 * time.Millisecond) + } + + err := obj.open() + if err != nil { + return err + } + + return nil +} + +func (obj *SerialStruct) Close() error { + *obj.connect = false + return obj.port.Close() +} diff --git a/timeouts.go b/timeouts.go new file mode 100644 index 0000000..f4ebc0b --- /dev/null +++ b/timeouts.go @@ -0,0 +1,11 @@ +package mpost + +import "time" + +//////////////////////////////////// + +const ( + CommunicationDisconnectTimeout = time.Second * 3 + PollingDisconnectTimeout = time.Second * 3 + CalibrateTimeout = time.Second * 3 +) diff --git a/verControl.go b/verControl.go deleted file mode 100644 index 8ab7f79..0000000 --- a/verControl.go +++ /dev/null @@ -1,12 +0,0 @@ -package mpost - -const ( - GlobalName string = "mpost" - GlobalDateUpdate string = "09-05-2024" - GlobalHash string = "9cf6b8c5447a34ff427159e035b9d58d9d27e904" - - GlobalVersion string = "v0.1.3" - GlobalVersionMajor string = "v0" - GlobalVersionMinor uint16 = 1 - GlobalVersionPatch uint16 = 3 -)