OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 132 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
83 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
83 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.core.sales.pos.io;
15
 
182 ilm 16
import org.openconcerto.erp.core.sales.pos.io.ConcertProtocol.ConcertStateListener.States;
17
import org.openconcerto.utils.StringUtils;
83 ilm 18
 
19
import java.io.ByteArrayOutputStream;
182 ilm 20
import java.io.FileInputStream;
21
import java.io.FileOutputStream;
83 ilm 22
import java.io.InputStream;
23
import java.io.OutputStream;
182 ilm 24
import java.util.ArrayList;
25
import java.util.List;
83 ilm 26
 
27
import org.apache.poi.util.HexDump;
28
 
182 ilm 29
import gnu.io.CommPort;
30
import gnu.io.CommPortIdentifier;
31
import gnu.io.SerialPort;
32
 
83 ilm 33
public class ConcertProtocol {
182 ilm 34
 
35
    public interface ConcertStateListener {
36
        enum States {
37
            ERROR, CONNECTED, WAITING, OK, NOK
38
        }
39
 
40
        void stateChanged(States state);
41
    }
42
 
83 ilm 43
    // Message start
44
    private static final char STX = (char) 0x02;
45
    // Message end
46
    private static final char ETX = (char) 0x03;
47
    // End of transmission
48
    private static final char EOT = (char) 0x04;
49
    // Start of transmission
50
    private static final char ENQ = (char) 0x05;
51
    // Positive ACK
52
    private static final char ACK = (char) 0x06;
53
    // Negative ACK
54
    private static final char NACK = (char) 0x15;
55
 
56
    // Mode
57
    private static final char MODE_CARD = '1';
58
 
59
    // Type
60
    private static final char TYPE_BUY = '0';
61
 
62
    // Currency
132 ilm 63
    public static final String CURRENCY_EUR = "978";
83 ilm 64
    private String port;
65
 
182 ilm 66
    private List<ConcertStateListener> listeners = new ArrayList<>();
67
 
83 ilm 68
    public ConcertProtocol(String port) {
69
        this.port = port;
70
    }
71
 
182 ilm 72
    public void addStateListener(ConcertStateListener listener) {
73
        listeners.add(listener);
74
    }
75
 
76
    private void fireStateChanged(States connected) {
77
        for (ConcertStateListener l : listeners)
78
            l.stateChanged(connected);
79
    }
80
 
132 ilm 81
    public synchronized boolean sendCardPayment(int amountInCents, String currency) throws Exception {
83 ilm 82
        if (currency == null) {
83
            currency = CURRENCY_EUR;
84
        }
85
        return sendPrototolE(1, amountInCents, true, MODE_CARD, TYPE_BUY, currency, "OpenConcerto");
86
    }
87
 
88
    public boolean sendPrototolE(int posIndex, int amountInCents, boolean requireResponse, char mode, char type, String currency, String string) throws Exception {
89
        boolean result = false;
90
        if (posIndex > 99 || posIndex < 0) {
182 ilm 91
            fireStateChanged(ConcertStateListener.States.ERROR);
83 ilm 92
            throw new IllegalArgumentException("Pos index must be between 0 and 99");
93
        }
94
        if (amountInCents < 0) {
182 ilm 95
            fireStateChanged(ConcertStateListener.States.ERROR);
83 ilm 96
            throw new IllegalArgumentException("Amount must be positive");
97
        }
98
        if (currency.length() != 3) {
182 ilm 99
            fireStateChanged(ConcertStateListener.States.ERROR);
83 ilm 100
            throw new IllegalArgumentException("Bad currency code : " + currency);
101
        }
102
 
182 ilm 103
        final OutputStream out;
104
        InputStream in;
105
        final SerialPort serialPort;
106
        if (isSerialPort()) {
107
            serialPort = getSerialPort();
108
            out = serialPort.getOutputStream();
109
            in = serialPort.getInputStream();
110
        } else {
111
            serialPort = null;
112
            in = new FileInputStream(this.port);
83 ilm 113
 
182 ilm 114
            if (in.available() > 0) {
115
                byte[] buffer = new byte[512];
116
                in.read(buffer);
117
                in.close();
118
            }
119
            in = new FileInputStream(this.port);
120
            out = new FileOutputStream(this.port);
121
        }
122
 
83 ilm 123
        out.write(ENQ);
124
 
125
        byte[] buffer = new byte[512];
126
        int nbRead = in.read(buffer);
127
 
128
        if (nbRead != 1 || buffer[0] != ACK) {
182 ilm 129
            String r = StringUtils.bytesToHexString(buffer, nbRead);
130
            in.close();
131
            out.close();
132
            if (serialPort != null) {
133
                serialPort.close();
134
            }
135
            fireStateChanged(ConcertStateListener.States.ERROR);
83 ilm 136
            throw new IllegalStateException("Bad response received : " + r);
137
        }
182 ilm 138
 
139
        fireStateChanged(ConcertStateListener.States.CONNECTED);
140
 
83 ilm 141
        //
142
        final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
143
        bOut.write(STX);
144
        // POS Index
145
        String n = rightAlign(posIndex, 2, '0');
146
        bOut.write(n.getBytes());
147
        // Amount in cents
148
        bOut.write(rightAlign(amountInCents, 8, '0').getBytes());
149
        if (requireResponse) {
150
            bOut.write('1');
151
        } else {
152
            bOut.write('0');
153
        }
154
        // Mode & type
155
        bOut.write(mode);
156
        bOut.write(type);
157
        // Currency
158
        bOut.write(currency.getBytes());
159
        // Text
160
        bOut.write(leftAlign(string, 10, ' ').getBytes());
161
        bOut.write(ETX);
162
        byte b = getLrc(bOut.toByteArray());
163
        bOut.write(b);
164
        out.write(bOut.toByteArray());
165
 
166
        // READ ACK
167
        nbRead = in.read(buffer);
168
        if (nbRead != 1 || buffer[0] != ACK) {
182 ilm 169
            String r = StringUtils.bytesToHexString(buffer, nbRead);
170
            in.close();
171
            out.close();
172
            if (serialPort != null) {
173
                serialPort.close();
174
            }
175
            fireStateChanged(ConcertStateListener.States.ERROR);
83 ilm 176
            throw new IllegalStateException("Bad response received : " + nbRead + ": " + r);
177
        }
178
 
179
        // END
180
        out.write(EOT);
181
 
182 ilm 182
        fireStateChanged(ConcertStateListener.States.WAITING);
183
 
83 ilm 184
        // Wait reply
185
        int count = 0;
186
        final int maxCount = 60 * 5;
187
        while (count < maxCount) {
188
            nbRead = in.read(buffer);
189
            if (nbRead > 0) {
190
                if (buffer[0] == ENQ) {
191
                    out.write(ACK);
192
                } else if (buffer[0] == STX) {
193
                    if (buffer[3] == '0') {
194
                        result = true;
195
                        out.write(ACK);
196
                        count = maxCount;
182 ilm 197
                        fireStateChanged(ConcertStateListener.States.OK);
83 ilm 198
                    } else if (buffer[3] == '7') {
199
                        out.write(NACK);
200
                        count = maxCount;
182 ilm 201
                        fireStateChanged(ConcertStateListener.States.NOK);
83 ilm 202
                    }
203
                } else if (buffer[0] == EOT) {
204
                    count = maxCount;
205
                }
206
            }
207
            Thread.sleep(200);
208
            count++;
209
 
210
        }
211
 
212
        out.close();
213
        in.close();
182 ilm 214
        if (serialPort != null) {
215
            serialPort.close();
216
        }
217
 
83 ilm 218
        return result;
219
    }
220
 
221
    private byte getLrc(byte[] bytes) {
182 ilm 222
        byte lrc = (byte) 0x0;
83 ilm 223
        for (int i = 1; i < bytes.length; i++) {
182 ilm 224
            lrc ^= bytes[i];
83 ilm 225
        }
182 ilm 226
        return lrc;
83 ilm 227
    }
228
 
182 ilm 229
    private boolean isSerialPort() {
230
        return (this.port.startsWith("/dev") || this.port.startsWith("COM"));
231
    }
232
 
83 ilm 233
    private SerialPort getSerialPort() throws Exception {
234
        if (port == null || port.length() == 0) {
235
            throw new IllegalStateException("Invalid serial port name: " + port);
236
        }
237
 
238
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(port);
239
        if (portIdentifier.isCurrentlyOwned()) {
240
            throw new IllegalAccessError("Port " + port + " is currently in use");
241
        }
242
        CommPort commPort = portIdentifier.open("CommUtil", 2000);
243
 
244
        if (!(commPort instanceof SerialPort)) {
245
            throw new IllegalStateException("Invalid serial port: " + port);
246
        }
247
 
248
        SerialPort serialPort = (SerialPort) commPort;
249
        serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
250
        return serialPort;
251
    }
252
 
253
    private static String rightAlign(int value, int maxWidth, char fillChar) {
254
        String r = String.valueOf(value);
255
        if (r.length() > maxWidth) {
256
            return r.substring(0, maxWidth);
257
        }
258
        int n = maxWidth - r.length();
259
        for (int i = 0; i < n; i++) {
260
            r = fillChar + r;
261
        }
262
        return r;
263
    }
264
 
265
    private static String leftAlign(String r, int maxWidth, char fillChar) {
266
        if (r.length() > maxWidth) {
267
            return r.substring(0, maxWidth);
268
        }
269
        int n = maxWidth - r.length();
270
        for (int i = 0; i < n; i++) {
271
            r += fillChar;
272
        }
273
        return r;
274
    }
132 ilm 275
 
276
    public static void main(String[] args) throws Exception {
277
        ConcertProtocol p = new ConcertProtocol("COM9");
278
        p.sendCardPayment(4579, ConcertProtocol.CURRENCY_EUR);
279
    }
83 ilm 280
}