├── codelingo.yaml ├── .gitignore ├── cmd └── ups │ ├── drucker.csv │ ├── xmlreq.xml │ ├── zpl │ ├── labels │ └── example.zpl │ └── main.go ├── .travis.yml ├── structs.go ├── http.go ├── LICENSE ├── load.go ├── README.md └── ups.go /codelingo.yaml: -------------------------------------------------------------------------------- 1 | tenets: 2 | - import: codelingo/go/ticker-in-for-select 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.exe~ 3 | *.dll 4 | *.so 5 | *.dylib 6 | *.test 7 | *.out 8 | cmd/ups/ups 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /cmd/ups/drucker.csv: -------------------------------------------------------------------------------- 1 | mndt;name;ip;port;info;dpi;peel 2 | 100;PRNWA132;192.168.178.132;9100;Warenausgang;300;true 3 | 100;PRNTEST100;127.0.0.1;9100;Test;300;false 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.11.x 5 | - tip 6 | 7 | script: 8 | - go get simonwaldherr.de/go/ups/cmd/ups 9 | - go fmt simonwaldherr.de/go/ups/cmd/ups 10 | - go build simonwaldherr.de/go/ups/cmd/ups -------------------------------------------------------------------------------- /cmd/ups/xmlreq.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PRNTEST100 5 | 1 6 | 7 | 8 | 1 9 | 10 | 11 | -------------------------------------------------------------------------------- /cmd/ups/zpl: -------------------------------------------------------------------------------- 1 | CT~~CD,~CC^~CT~ 2 | ^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR12,12~SD20^JUS^LRN^CI0^XZ 3 | ^XA 4 | ^MMT 5 | ^PW900 6 | ^LL0600 7 | ^LS0 8 | ^BY5,3,236^FT48,590^BCN,,N,N 9 | ^FD>;1>68^FS 10 | ^BY5,3,236^FT873,590^BCB,,N,N 11 | ^FD>;1>68^FS 12 | ^FT13,141^A0N,100,96^FH\^FD1^FS 13 | ^PQ,,,Y^XZ 14 | 15 | 16 | -------------------------------------------------------------------------------- /cmd/ups/labels/example.zpl: -------------------------------------------------------------------------------- 1 | CT~~CD,~CC^~CT~ 2 | ^XA~TA000~JSN^LT0^MNW^MTD^PON^PMN^LH0,0^JMA^PR8,8~SD20^JUS^LRN^CI0^XZ 3 | ^XA 4 | ^MMT 5 | ^PW900 6 | ^LL0600 7 | ^LS0 8 | ^BY5,3,236^FT48,590^BCN,,N,N 9 | ^FD>;$NUMBER$>68^FS 10 | ^BY5,3,236^FT873,590^BCB,,N,N 11 | ^FD>;$NUMBER$>68^FS 12 | ^FT13,141^A0N,100,96^FH\^FD$NUMBER$^FS 13 | ^PQ,,,Y^XZ 14 | -------------------------------------------------------------------------------- /cmd/ups/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "simonwaldherr.de/go/ups" 7 | //ups "../../../ups" 8 | ) 9 | 10 | func main() { 11 | ups.LogInit(os.Stdout, os.Stderr, os.Stderr) 12 | 13 | ups.Hub.Init() 14 | 15 | log.SetFlags(log.Ltime | log.Lshortfile) 16 | 17 | ups.Labels, ups.Ltemplate = ups.ParseLabels("labels") 18 | 19 | ups.Printer = ups.LoadPrinter("drucker.csv") 20 | 21 | go ups.PrintMessages() 22 | go ups.InitTelnet() 23 | ups.InitHTTP() 24 | } 25 | -------------------------------------------------------------------------------- /structs.go: -------------------------------------------------------------------------------- 1 | package ups 2 | 3 | type Variables struct { 4 | Head Head 5 | Data classAccessesMap `xml:"Data"` 6 | } 7 | 8 | type Head struct { 9 | Label string 10 | Printer string 11 | Count string 12 | } 13 | 14 | type classAccessesMap struct { 15 | Map map[string]string 16 | } 17 | 18 | type Device struct { 19 | Mandt string 20 | Name string 21 | IP string 22 | Port string 23 | Info string 24 | DPI int 25 | Peel bool 26 | } 27 | 28 | type Devices struct { 29 | Devs map[string]*Device 30 | } 31 | 32 | type LogMsg struct { 33 | Date string 34 | Str string 35 | Msgtype string 36 | Dst string 37 | Ip string 38 | Label string 39 | Weight string 40 | } 41 | 42 | type Connections struct { 43 | clients map[chan LogMsg]bool 44 | addClient chan chan LogMsg 45 | removeClient chan chan LogMsg 46 | messages chan LogMsg 47 | } 48 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package ups 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "strconv" 9 | ) 10 | 11 | func HttpPostRequest(url, msgbody string) (string, error) { 12 | client := &http.Client{} 13 | body := bytes.NewBufferString(msgbody) 14 | clength := strconv.Itoa(len(msgbody)) 15 | r, _ := http.NewRequest("POST", url, body) 16 | r.Header.Add("User-Agent", "NicerWatch") 17 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 18 | r.Header.Add("Content-Length", clength) 19 | 20 | rsp, err := client.Do(r) 21 | 22 | if err != nil { 23 | return "", err 24 | } 25 | 26 | defer func() { 27 | rsp.Body.Close() 28 | }() 29 | if rsp.StatusCode == 200 { 30 | bodyBytes, err := ioutil.ReadAll(rsp.Body) 31 | return fmt.Sprintf("%v", bodyBytes), err 32 | } else if err != nil { 33 | return "", err 34 | } else { 35 | return "", fmt.Errorf("The remote end did not return a HTTP 200 (OK) response:%#v\n", rsp) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Simon Waldherr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /load.go: -------------------------------------------------------------------------------- 1 | package ups 2 | 3 | import ( 4 | "encoding/xml" 5 | "fmt" 6 | "path/filepath" 7 | "simonwaldherr.de/go/golibs/as" 8 | "simonwaldherr.de/go/golibs/csv" 9 | "simonwaldherr.de/go/golibs/file" 10 | "strings" 11 | ) 12 | 13 | func CreateDeviceMap() *Devices { 14 | dev := make(map[string]*Device) 15 | return &Devices{ 16 | Devs: dev, 17 | } 18 | } 19 | 20 | func (devices *Devices) Set(Mandant, Name, IP, Port, Info string, DPI int, PeelOff bool) { 21 | devices.Devs[Name] = &Device{ 22 | Mandt: Mandant, 23 | Name: Name, 24 | IP: IP, 25 | Port: Port, 26 | Info: Info, 27 | DPI: DPI, 28 | Peel: PeelOff, 29 | } 30 | } 31 | 32 | func ParseLabels(labeldir string) ([]string, map[string]string) { 33 | var labelsLocal []string 34 | var ltemplateLocal = make(map[string]string) 35 | labelsLocal, _ = file.ReadDir(filepath.Join(homedir, labeldir)) 36 | fmt.Println("###### Labels ######") 37 | for _, name := range labelsLocal { 38 | if strings.Contains(name, ".200zpl") || strings.Contains(name, ".300zpl") || strings.Contains(name, ".zpl") { 39 | fmt.Printf("* %v\n", name) 40 | str, _ := file.Read(filepath.Join(homedir, labeldir, name)) 41 | name = normalizeLabelName(name) 42 | ltemplateLocal[name] = str 43 | } 44 | } 45 | fmt.Println() 46 | return labelsLocal, ltemplateLocal 47 | } 48 | 49 | func LoadPrinter(filename string) *Devices { 50 | dev := CreateDeviceMap() 51 | csvdata, k := csv.LoadCSVfromFile(filename) 52 | fmt.Println("###### Printer ######") 53 | 54 | for _, data := range csvdata { 55 | mndt := as.String(data[k["mndt"]]) 56 | name := as.String(data[k["name"]]) 57 | ip := as.String(data[k["ip"]]) 58 | port := as.String(data[k["port"]]) 59 | info := as.String(data[k["info"]]) 60 | dpi := int(as.Int(data[k["dpi"]])) 61 | peel := as.Bool(data[k["peel"]]) 62 | fmt.Printf("* %v\n", name) 63 | dev.Set(mndt, name, ip, port, info, dpi, peel) 64 | } 65 | 66 | fmt.Println() 67 | 68 | return dev 69 | } 70 | 71 | func (c *classAccessesMap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 72 | c.Map = map[string]string{} 73 | val := "" 74 | 75 | for { 76 | t, _ := d.Token() 77 | switch tt := t.(type) { 78 | 79 | case xml.StartElement: 80 | 81 | case xml.EndElement: 82 | if tt.Name == start.Name { 83 | return nil 84 | } 85 | c.Map[tt.Name.Local] = val 86 | default: 87 | val = strings.TrimSpace(fmt.Sprintf("%s", tt)) 88 | } 89 | } 90 | } 91 | 92 | func ParseDocumentXML(xmlString string) Variables { 93 | m := Variables{} 94 | xml.Unmarshal([]byte(xmlString), &m) 95 | return m 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UPS - Uncommon Printing System 2 | 3 | *a simple printing system for labels* 4 | 5 | [![DOI](https://zenodo.org/badge/153938980.svg)](https://doi.org/10.5281/zenodo.15291217) 6 | [![GoDoc](https://godoc.org/github.com/SimonWaldherr/ups?status.svg)](https://godoc.org/github.com/SimonWaldherr/ups) 7 | 8 | I wrote the [Uncommon Printing System](https://simonwaldherr.de/go/ups) a long time ago to replace a proprietary printing system called NiceWatch by [NiceLabel](https://www.nicelabel.com). 9 | Don't get me wrong, [Nice Label Designer](https://www.nicelabel.com/design-and-print) is still the best WYSIWYG Label Editor on Earth, but NiceWatch is slow and unstable. 10 | The UPS is programmed to support Label Templates designed with NiceLabel Designer and print them on ZPL compatible printers. 11 | 12 | This repository contains a refactored version of **UPS**. It only contains general purpose features, features like: 13 | 14 | * handling invalid XML-Files from SAP systems 15 | * reload missing data in XML-Files from SAP systems 16 | * save log-data to a BI (business intelligence) system 17 | * transfer material master data to a sub-system 18 | * load printer data from a SAP database table 19 | 20 | are not included. 21 | 22 | I use UPS in a customized version to print up to 10000 labels daily. 23 | UPS can also do a lot more with ease, but in the current case of application it is not needed. 24 | You can even run UPS on a Raspberry Pi. 25 | 26 | Currently I only print on Zebra ZM400, Zebra ZT410, Zebra QL 420 (plus) and Zebra QLn 420, but I plan to extend the UPS to support non ZPL printers as well. 27 | 28 | ## Test it 29 | 30 | to test the application you can simply follow these steps: 31 | 32 | 1. ```go get simonwaldherr.de/go/ups/cmd/ups``` 33 | 1. ```ups &``` 34 | 1. ```nc -l 9100 > zpl &``` 35 | 1. ```cat xmlreq.xml | nc localhost 30000``` 36 | 1. ```kill -9 $(pidof ups)``` 37 | 38 | ## Why 39 | 40 | Why what? Why I wrote the application? Because I did not want to bother anymore with an unstable proprietary software! 41 | I wasted several hours a week managing the NiceWatch system and keeping it running. 42 | I had much better things to do and that's why I wrote UPS. 43 | 44 | Why I made some choices the way I made them? Mostly there is also a reasonable cause - you want an example? 45 | There is a function called ```cdatafy``` which adds ```CDATA```-sections to the XML-string. 46 | You may ask why on earth someone would need such a function. 47 | Because many SAP developers don't know anything about XML in general and XML marshalling in specific, 48 | so they just concatenate strings and as result they create invalid XMLs. 49 | It seems like the favorite word of most SAP consultants is standard, 50 | but when it comes to actual W3C, WHATWG, IETF, ISO, … standards, they do not care. 51 | 52 | ## But 53 | 54 | If you only need a tool to print images on a Label-printer you can use this tool: 55 | [![SimonWaldherr/zplgfa - GitHub](https://gh-card.dev/repos/SimonWaldherr/zplgfa.svg?fullname)](https://github.com/SimonWaldherr/zplgfa) 56 | 57 | 58 | ## License 59 | 60 | [MIT](https://github.com/SimonWaldherr/ups/blob/master/LICENSE) 61 | -------------------------------------------------------------------------------- /ups.go: -------------------------------------------------------------------------------- 1 | package ups 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "encoding/json" 7 | _ "expvar" 8 | "flag" 9 | "fmt" 10 | "image" 11 | _ "image/gif" 12 | _ "image/jpeg" 13 | _ "image/png" 14 | "io" 15 | "io/ioutil" 16 | "log" 17 | "net" 18 | "net/http" 19 | "os" 20 | "path/filepath" 21 | "simonwaldherr.de/go/golibs/as" 22 | "simonwaldherr.de/go/golibs/cache" 23 | "simonwaldherr.de/go/golibs/file" 24 | "simonwaldherr.de/go/golibs/regex" 25 | "simonwaldherr.de/go/zplgfa" 26 | "strings" 27 | "time" 28 | ) 29 | 30 | var cacheXML *cache.Cache 31 | var cacheMAT *cache.Cache 32 | var cacheTXT *cache.Cache 33 | 34 | var portWaage string = ":56429" 35 | 36 | var Hub = &Connections{ 37 | clients: make(map[chan LogMsg]bool), 38 | addClient: make(chan (chan LogMsg)), 39 | removeClient: make(chan (chan LogMsg)), 40 | messages: make(chan LogMsg), 41 | } 42 | 43 | func (hub *Connections) Init() { 44 | go func() { 45 | for { 46 | select { 47 | case s := <-hub.addClient: 48 | hub.clients[s] = true 49 | Info.Println("Added new client") 50 | 51 | case s := <-hub.removeClient: 52 | delete(hub.clients, s) 53 | Info.Println("Removed client") 54 | 55 | case msg := <-hub.messages: 56 | for s := range hub.clients { 57 | s <- msg 58 | } 59 | } 60 | } 61 | }() 62 | } 63 | 64 | func normalizeLabelName(name string) string { 65 | name = strings.ToLower(name) 66 | name, _ = regex.ReplaceAllString(name, "[ _-]", "") 67 | name = strings.TrimSpace(name) 68 | return name 69 | } 70 | 71 | var ( 72 | Info *log.Logger 73 | Warning *log.Logger 74 | Error *log.Logger 75 | ) 76 | 77 | func LogInit( 78 | infoHandle io.Writer, 79 | warningHandle io.Writer, 80 | errorHandle io.Writer) { 81 | 82 | Info = log.New(infoHandle, 83 | "INFO: ", 84 | log.Ltime|log.Lshortfile) 85 | 86 | Warning = log.New(warningHandle, 87 | "WARNING: ", 88 | log.Ltime|log.Lshortfile) 89 | 90 | Error = log.New(errorHandle, 91 | "ERROR: ", 92 | log.Ldate|log.Ltime|log.Lshortfile) 93 | } 94 | 95 | var infoserver string 96 | var homedir string 97 | var goos string 98 | var testdrucker string 99 | 100 | const timeout int64 = 32 101 | const retrytime = 510 * time.Millisecond 102 | 103 | var sonderzeichen = map[string]string{ 104 | "Ö": "\\99", 105 | "ö": "\\94", 106 | "Ü": "\\9A", 107 | "ü": "\\81", 108 | "Ä": "\\8E", 109 | "ä": "\\84", 110 | "ß": "\\E1", 111 | "Ø": "\\9D", 112 | "µ": "\\E6", 113 | "~": "\\7E", 114 | } 115 | 116 | type msgch struct { 117 | msg string 118 | ip string 119 | port string 120 | intime int64 121 | } 122 | 123 | var msgchan = make(chan msgch, 64) 124 | var Labels []string 125 | var Ltemplate = make(map[string]string) 126 | var Printer *Devices 127 | 128 | func toUtf8(iso8859 []byte) string { 129 | buf := make([]rune, len(iso8859)) 130 | for i, b := range iso8859 { 131 | buf[i] = rune(b) 132 | } 133 | return string(buf) 134 | } 135 | 136 | func sendLabelToZebra(ip, port, printertype, zpl string, retry int) bool { 137 | var servAddr string 138 | servAddr = ip + ":" + port 139 | 140 | Info.Println(servAddr) 141 | 142 | tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr) 143 | Warning.Println(err) 144 | conn, err := net.DialTCP("tcp4", nil, tcpAddr) 145 | if err == nil { 146 | defer conn.Close() 147 | payloadBytes := []byte(fmt.Sprintf("%s\r\n\r\n", zpl)) 148 | if _, err = conn.Write(payloadBytes); err != nil { 149 | Info.Println(err) 150 | 151 | if retry > 0 { 152 | Warning.Printf("pos: 1 ip: %v retry: %v err: %v", ip, retry, err) 153 | 154 | time.Sleep(retrytime) 155 | return sendLabelToZebra(ip, port, printertype, zpl, retry-1) 156 | } 157 | } 158 | return true 159 | } 160 | Warning.Println(err) 161 | 162 | if retry > 0 { 163 | Warning.Printf("pos: 2 ip: %v retry: %v err: %v", ip, retry, err) 164 | 165 | time.Sleep(retrytime) 166 | return sendLabelToZebra(ip, port, printertype, zpl, retry-1) 167 | } 168 | return false 169 | } 170 | 171 | func sendDataToZebra(ip, port, printertype, str string) bool { 172 | var servAddr string 173 | 174 | servAddr = ip + ":" + port 175 | 176 | Info.Println(servAddr) 177 | 178 | tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr) 179 | Warning.Println(err) 180 | conn, err := net.DialTCP("tcp4", nil, tcpAddr) 181 | if err == nil { 182 | defer conn.Close() 183 | 184 | payloadBytes := []byte(fmt.Sprintf("%s\r\n\r\n", str)) 185 | if _, err = conn.Write(payloadBytes); err != nil { 186 | Info.Println(err) 187 | } 188 | return true 189 | } 190 | Warning.Println(err) 191 | return false 192 | } 193 | 194 | func sendFeedCmdToZebra(ip, port, printertype string) bool { 195 | return sendDataToZebra(ip, port, printertype, "^xa^aa^fd ^fs^xz") 196 | } 197 | 198 | func sendCalibCmdToZebra(ip, port, printertype string) bool { 199 | return sendDataToZebra(ip, port, printertype, "~jc^xa^jus^xz") 200 | } 201 | 202 | func sendCmdToZebra(data, printername string) bool { 203 | if _, ok := Printer.Devs[printername]; ok { 204 | printertype := PrinterType(printername) 205 | return sendDataToZebra(Printer.Devs[printername].IP, Printer.Devs[printername].Port, printertype, data) 206 | } 207 | return false 208 | } 209 | 210 | func getInfoFromZebra(ip, port string, retry int) string { 211 | zpl := "~HS" 212 | servAddr := ip + ":" + port 213 | Info.Println(servAddr) 214 | 215 | tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr) 216 | Warning.Println(err) 217 | conn, err := net.DialTCP("tcp4", nil, tcpAddr) 218 | if err == nil { 219 | defer conn.Close() 220 | 221 | payloadBytes := []byte(fmt.Sprintf("%s\r\n\r\n", zpl)) 222 | if _, err = conn.Write(payloadBytes); err != nil { 223 | Warning.Println(err) 224 | 225 | if retry > 0 { 226 | Warning.Printf("pos: 1 ip: %v retry: %v err: %v", ip, retry, err) 227 | 228 | time.Sleep(retrytime) 229 | return getInfoFromZebra(ip, port, retry-1) 230 | } 231 | } 232 | message, err := bufio.NewReader(conn).ReadString('\n') 233 | if err != nil { 234 | return "" 235 | } 236 | return message 237 | } 238 | Warning.Println(err) 239 | 240 | if retry > 0 { 241 | Warning.Printf("pos: 2 ip: %v retry: %v err: %v", ip, retry, err) 242 | 243 | time.Sleep(retrytime) 244 | return getInfoFromZebra(ip, port, retry-1) 245 | } 246 | return "" 247 | } 248 | 249 | func toISODate(str string) string { 250 | if str == "00000000" { 251 | return "" 252 | } 253 | if len(str) == 8 { 254 | return str[0:4] + "-" + str[4:6] + "-" + str[6:8] 255 | } 256 | return str 257 | } 258 | 259 | func handleTCPConnection(c net.Conn) { 260 | defer c.Close() 261 | c.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout))) 262 | buf := make([]byte, 4096) 263 | for { 264 | n, err := c.Read(buf) 265 | if (err != nil) || (n == 0) { 266 | c.Close() 267 | break 268 | } 269 | Info.Println("handleTCPConnection") 270 | 271 | if n > 20 { 272 | msgchan <- msgch{ 273 | msg: fmt.Sprintf("%v%v", c.RemoteAddr(), string(buf[0:n])), 274 | ip: strings.Split(as.String(c.RemoteAddr()), ":")[0], 275 | port: strings.Split(as.String(c.RemoteAddr()), ":")[1], 276 | intime: time.Now().Unix(), 277 | } 278 | } 279 | } 280 | time.Sleep(150 * time.Millisecond) 281 | c.Close() 282 | Info.Printf("Connection from %v closed.\n", c.RemoteAddr()) 283 | } 284 | 285 | func handleHTTPConnection(rw http.ResponseWriter, req *http.Request) { 286 | postdata, err := ioutil.ReadAll(req.Body) 287 | if err == nil { 288 | str := strings.TrimSpace(string(postdata)) 289 | if str != "" { 290 | if strings.Contains(str, "") { 291 | msgchan <- msgch{ 292 | msg: fmt.Sprintf("%v%v", req.RemoteAddr, str), 293 | ip: strings.Split(as.String(req.RemoteAddr), ":")[0], 294 | port: strings.Split(as.String(req.RemoteAddr), ":")[1], 295 | intime: time.Now().Unix(), 296 | } 297 | return 298 | } 299 | } 300 | } 301 | f, ok := rw.(http.Flusher) 302 | if !ok { 303 | http.Error(rw, "Streaming not supported!", http.StatusInternalServerError) 304 | return 305 | } 306 | 307 | if req.URL.Path == "/" { 308 | rw.Header().Set("Server", "NicerWatch 0.998") 309 | rw.Header().Set("Content-Type", "text/html; charset=UTF-8") 310 | rw.WriteHeader(200) 311 | str, _ := file.Read(filepath.Join(homedir, "index.html")) 312 | fmt.Fprintf(rw, str) 313 | 314 | f.Flush() 315 | return 316 | } else if req.URL.Path == "/reloadPrinter" { 317 | Printer = LoadPrinter("drucker.txt") 318 | rw.Header().Set("Server", "NicerWatch 0.998") 319 | rw.Header().Set("Content-Type", "text/html; charset=UTF-8") 320 | rw.WriteHeader(200) 321 | fmt.Fprintf(rw, fmt.Sprintf("%#v", Printer)) 322 | 323 | f.Flush() 324 | return 325 | } else if req.URL.Path == "/reloadLabels" { 326 | Labels, Ltemplate = ParseLabels("labels") 327 | rw.Header().Set("Server", "NicerWatch 0.998") 328 | rw.Header().Set("Content-Type", "text/html; charset=UTF-8") 329 | rw.WriteHeader(200) 330 | fmt.Fprintf(rw, fmt.Sprintf("%#v", Labels)) 331 | 332 | f.Flush() 333 | return 334 | } else if len(req.URL.Path) > 6 && req.URL.Path[0:6] == "/send/" { 335 | path := strings.Split(strings.Replace(req.URL.Path, "/send/", "", 1), "/") 336 | if path[0] == "calibrate" { 337 | sendCmdToZebra("~jc^xa^jus^xz", path[1]) 338 | } else if path[0] == "feed" { 339 | sendCmdToZebra("^xa^aa^fd ^fs^xz", path[1]) 340 | } 341 | rw.Header().Set("Server", "NicerWatch 0.998") 342 | rw.Header().Set("Content-Type", "text/html; charset=UTF-8") 343 | rw.WriteHeader(200) 344 | 345 | f.Flush() 346 | return 347 | } 348 | 349 | messageChannel := make(chan LogMsg) 350 | Hub.addClient <- messageChannel 351 | notify := rw.(http.CloseNotifier).CloseNotify() 352 | 353 | rw.Header().Set("Content-Type", "text/event-stream") 354 | rw.Header().Set("Cache-Control", "no-cache") 355 | rw.Header().Set("Connection", "keep-alive") 356 | 357 | tickerDuration := time.Second * 60 358 | ticker := time.NewTimer(tickerDuration) 359 | for i := 0; i < 1440; { 360 | fmt.Println("foo", i) 361 | ticker.Reset(tickerDuration) 362 | select { 363 | case msg := <-messageChannel: 364 | ticker.Stop() 365 | jsonData, _ := json.Marshal(msg) 366 | str := string(jsonData) 367 | str = fmt.Sprintf("data: {\"str\": %s,\"time\": \"%v\",\"x\": \"\"}\n\n", str, time.Now()) 368 | if req.URL.Path == "/events/sse" { 369 | fmt.Fprint(rw, str) 370 | } else if req.URL.Path == "/events/lp" { 371 | fmt.Fprint(rw, str) 372 | } 373 | f.Flush() 374 | case <-ticker.C: 375 | if req.URL.Path == "/events/sse" { 376 | fmt.Fprintf(rw, "data: {\"str\": \"No Data\"}\n\n") 377 | } else if req.URL.Path == "/events/lp" { 378 | fmt.Fprintf(rw, "{\"str\": \"No Data\"}") 379 | } 380 | f.Flush() 381 | i++ 382 | case <-notify: 383 | ticker.Stop() 384 | f.Flush() 385 | i = 1440 386 | Hub.removeClient <- messageChannel 387 | } 388 | } 389 | } 390 | 391 | func isZPLprintable(label string) bool { 392 | if _, ok := Ltemplate[label]; ok { 393 | return true 394 | } 395 | return false 396 | } 397 | 398 | func cdatafy(xml string, ele ...string) string { 399 | for _, element := range ele { 400 | xml = strings.Replace(xml, "", "]]>", -1) 401 | xml = strings.Replace(xml, "<"+element+">", "<"+element+">