├── 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 | [](https://doi.org/10.5281/zenodo.15291217)
6 | [](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 | [](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, ""+element+">", "]]>"+element+">", -1)
401 | xml = strings.Replace(xml, "<"+element+">", "<"+element+">