@padosoft/react-native-ecr17 0.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Ecr17.podspec +39 -39
- package/README.md +348 -348
- package/android/CMakeLists.txt +41 -41
- package/android/build.gradle +148 -148
- package/android/fix-prefab.gradle +50 -50
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/cpp/cpp-adapter.cpp +8 -8
- package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
- package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
- package/cpp/Ecr17.hpp +1 -1
- package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
- package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
- package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
- package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
- package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
- package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
- package/cpp/Lcr/Lcr.cpp +42 -42
- package/cpp/Lcr/Lcr.hpp +21 -21
- package/cpp/PacketCodec/PacketCodec.cpp +145 -145
- package/cpp/PacketCodec/PacketCodec.hpp +47 -47
- package/cpp/Session/Ecr17Session.cpp +260 -260
- package/cpp/Session/Ecr17Session.hpp +97 -97
- package/cpp/Session/RetryPolicy.hpp +23 -23
- package/cpp/Transport/FakeTransport.hpp +95 -95
- package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
- package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
- package/cpp/Transport/Transport.hpp +30 -30
- package/cpp/tests/CMakeLists.txt +55 -55
- package/cpp/tests/PosixTcpTransport.hpp +105 -105
- package/cpp/tests/stubs/LrcMode.hpp +25 -25
- package/cpp/tests/test_flows.cpp +148 -148
- package/cpp/tests/test_integration_terminal.cpp +72 -72
- package/cpp/tests/test_lrc.cpp +66 -66
- package/cpp/tests/test_packet_codec.cpp +164 -164
- package/cpp/tests/test_protocol.cpp +102 -102
- package/cpp/tests/test_protocol_commands.cpp +190 -190
- package/cpp/tests/test_response.cpp +164 -164
- package/cpp/tests/test_retry_policy.cpp +28 -28
- package/cpp/tests/test_session.cpp +262 -262
- package/ios/HybridEcr17Transport.swift +103 -103
- package/lib/commonjs/index.js +50 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/client.nitro.js +17 -0
- package/lib/commonjs/specs/client.nitro.js.map +1 -0
- package/lib/commonjs/specs/transport.nitro.js +6 -0
- package/lib/commonjs/specs/transport.nitro.js.map +1 -0
- package/lib/commonjs/types/client.js +2 -0
- package/lib/commonjs/types/client.js.map +1 -0
- package/lib/commonjs/utils/client.js +13 -0
- package/lib/commonjs/utils/client.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/client.nitro.js +13 -0
- package/lib/module/specs/client.nitro.js.map +1 -0
- package/lib/module/specs/transport.nitro.js +4 -0
- package/lib/module/specs/transport.nitro.js.map +1 -0
- package/lib/module/types/client.js +2 -0
- package/lib/module/types/client.js.map +1 -0
- package/lib/module/utils/client.js +9 -0
- package/lib/module/utils/client.js.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
- package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types/client.d.ts +138 -0
- package/lib/typescript/src/types/client.d.ts.map +1 -0
- package/lib/typescript/src/utils/client.d.ts +3 -0
- package/lib/typescript/src/utils/client.d.ts.map +1 -0
- package/nitro.json +30 -30
- package/package.json +4 -4
- package/react-native.config.js +18 -18
- package/src/index.ts +4 -4
- package/src/specs/client.nitro.ts +102 -102
- package/src/specs/transport.nitro.ts +25 -25
- package/src/types/client.ts +196 -196
- package/src/utils/client.ts +10 -10
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <cstdint>
|
|
4
|
-
#include <string>
|
|
5
|
-
|
|
6
|
-
// Parsers for ECR17 terminal *response* application messages. They take the
|
|
7
|
-
// application payload (the bytes between STX and ETX, i.e. DecodedPacket.payload)
|
|
8
|
-
// and return plain C++ structs — intentionally independent of Nitro so they can
|
|
9
|
-
// be unit-tested standalone. HybridEcr17Client maps these to the generated Nitro
|
|
10
|
-
// result types. Field offsets follow the spec response tables in docs/.
|
|
11
|
-
//
|
|
12
|
-
// Parsing is defensive: fields beyond the payload length come back empty rather
|
|
13
|
-
// than throwing, so a short/truncated response degrades gracefully.
|
|
14
|
-
|
|
15
|
-
namespace margelo::nitro::ecr17 {
|
|
16
|
-
|
|
17
|
-
enum class Outcome {
|
|
18
|
-
Ok, // "00"
|
|
19
|
-
Ko, // "01"
|
|
20
|
-
CardNotPresent, // "05"
|
|
21
|
-
UnknownTag, // "09"
|
|
22
|
-
Unknown, // anything else / missing
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
Outcome outcomeFromCode(const std::string& code);
|
|
26
|
-
|
|
27
|
-
// Optional DCC / currency-exchange block (parsed from a 'V'/'v' response).
|
|
28
|
-
// Named DccInfo (not CurrencyExchange) to avoid clashing with the Nitro-generated
|
|
29
|
-
// CurrencyExchange struct in the same namespace.
|
|
30
|
-
struct DccInfo {
|
|
31
|
-
bool applied = false;
|
|
32
|
-
std::string rate; // 8 digits, 4 decimals
|
|
33
|
-
std::string currencyCode; // alpha-3
|
|
34
|
-
std::string amount; // 12 digits, in transaction currency
|
|
35
|
-
std::string precision; // decimals
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Result of a payment-family response ('E' without DCC, 'V' with DCC). Reused
|
|
39
|
-
// for reversal / card verification / pre-auth closure which share the layout.
|
|
40
|
-
struct PaymentResponse {
|
|
41
|
-
Outcome outcome = Outcome::Unknown;
|
|
42
|
-
std::string resultCode; // raw "00"/"01"/"05"/"09"
|
|
43
|
-
std::string pan; // positive
|
|
44
|
-
std::string transactionType; // positive, raw "ICC"/"MAG"/...
|
|
45
|
-
std::string authCode; // positive
|
|
46
|
-
std::string hostDateTime; // positive, raw DDDHHMM
|
|
47
|
-
std::string errorDescription; // negative
|
|
48
|
-
std::string cardType; // common, raw "1"/"2"/"3"
|
|
49
|
-
std::string acquirerId; // common
|
|
50
|
-
std::string stan; // common
|
|
51
|
-
std::string onlineId; // common
|
|
52
|
-
DccInfo currency; // only when DCC present
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
struct StatusResponse {
|
|
56
|
-
std::string terminalId;
|
|
57
|
-
std::string dateTimeRaw; // "DDMMYYhhmm"
|
|
58
|
-
int status = -1; // 0..6, -1 = unknown/missing
|
|
59
|
-
std::string softwareRelease;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
struct TotalsResponse {
|
|
63
|
-
Outcome outcome = Outcome::Unknown;
|
|
64
|
-
std::string resultCode;
|
|
65
|
-
std::string posTotal; // 16 digits, cents
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
struct CloseResponse {
|
|
69
|
-
Outcome outcome = Outcome::Unknown;
|
|
70
|
-
std::string resultCode;
|
|
71
|
-
std::string posTotal; // positive, 16 digits
|
|
72
|
-
std::string hostTotal; // positive, 16 digits
|
|
73
|
-
std::string errorDescription; // negative
|
|
74
|
-
std::string actionCode; // negative
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
struct PreAuthResponse {
|
|
78
|
-
Outcome outcome = Outcome::Unknown;
|
|
79
|
-
std::string resultCode;
|
|
80
|
-
std::string pan;
|
|
81
|
-
std::string transactionType;
|
|
82
|
-
std::string authCode;
|
|
83
|
-
std::string preAuthorizedAmount; // 8 digits, cents
|
|
84
|
-
std::string preAuthCode; // 9 digits, unique pre-auth id
|
|
85
|
-
std::string actionCode;
|
|
86
|
-
std::string hostDateTime;
|
|
87
|
-
std::string errorDescription;
|
|
88
|
-
std::string cardType;
|
|
89
|
-
std::string acquirerId;
|
|
90
|
-
std::string stan;
|
|
91
|
-
std::string onlineId;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
struct VasResponse {
|
|
95
|
-
std::string responseId; // RESPID parsed from XML ("0" = OK), "" if absent
|
|
96
|
-
std::string responseMessage; // RESPMSG
|
|
97
|
-
std::string orderId; // ORDER_ID
|
|
98
|
-
bool moreMessages = false; // concatenation flag "1"
|
|
99
|
-
std::string idMessage; // 3-digit sequence
|
|
100
|
-
std::string rawXml; // the XML body of this message
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
class Ecr17Response {
|
|
104
|
-
public:
|
|
105
|
-
static PaymentResponse parsePayment(const std::string& payload);
|
|
106
|
-
static StatusResponse parseStatus(const std::string& payload);
|
|
107
|
-
static TotalsResponse parseTotals(const std::string& payload);
|
|
108
|
-
static CloseResponse parseClose(const std::string& payload);
|
|
109
|
-
static PreAuthResponse parsePreAuth(const std::string& payload);
|
|
110
|
-
static VasResponse parseVas(const std::string& payload);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
} // namespace margelo::nitro::ecr17
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
// Parsers for ECR17 terminal *response* application messages. They take the
|
|
7
|
+
// application payload (the bytes between STX and ETX, i.e. DecodedPacket.payload)
|
|
8
|
+
// and return plain C++ structs — intentionally independent of Nitro so they can
|
|
9
|
+
// be unit-tested standalone. HybridEcr17Client maps these to the generated Nitro
|
|
10
|
+
// result types. Field offsets follow the spec response tables in docs/.
|
|
11
|
+
//
|
|
12
|
+
// Parsing is defensive: fields beyond the payload length come back empty rather
|
|
13
|
+
// than throwing, so a short/truncated response degrades gracefully.
|
|
14
|
+
|
|
15
|
+
namespace margelo::nitro::ecr17 {
|
|
16
|
+
|
|
17
|
+
enum class Outcome {
|
|
18
|
+
Ok, // "00"
|
|
19
|
+
Ko, // "01"
|
|
20
|
+
CardNotPresent, // "05"
|
|
21
|
+
UnknownTag, // "09"
|
|
22
|
+
Unknown, // anything else / missing
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
Outcome outcomeFromCode(const std::string& code);
|
|
26
|
+
|
|
27
|
+
// Optional DCC / currency-exchange block (parsed from a 'V'/'v' response).
|
|
28
|
+
// Named DccInfo (not CurrencyExchange) to avoid clashing with the Nitro-generated
|
|
29
|
+
// CurrencyExchange struct in the same namespace.
|
|
30
|
+
struct DccInfo {
|
|
31
|
+
bool applied = false;
|
|
32
|
+
std::string rate; // 8 digits, 4 decimals
|
|
33
|
+
std::string currencyCode; // alpha-3
|
|
34
|
+
std::string amount; // 12 digits, in transaction currency
|
|
35
|
+
std::string precision; // decimals
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Result of a payment-family response ('E' without DCC, 'V' with DCC). Reused
|
|
39
|
+
// for reversal / card verification / pre-auth closure which share the layout.
|
|
40
|
+
struct PaymentResponse {
|
|
41
|
+
Outcome outcome = Outcome::Unknown;
|
|
42
|
+
std::string resultCode; // raw "00"/"01"/"05"/"09"
|
|
43
|
+
std::string pan; // positive
|
|
44
|
+
std::string transactionType; // positive, raw "ICC"/"MAG"/...
|
|
45
|
+
std::string authCode; // positive
|
|
46
|
+
std::string hostDateTime; // positive, raw DDDHHMM
|
|
47
|
+
std::string errorDescription; // negative
|
|
48
|
+
std::string cardType; // common, raw "1"/"2"/"3"
|
|
49
|
+
std::string acquirerId; // common
|
|
50
|
+
std::string stan; // common
|
|
51
|
+
std::string onlineId; // common
|
|
52
|
+
DccInfo currency; // only when DCC present
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
struct StatusResponse {
|
|
56
|
+
std::string terminalId;
|
|
57
|
+
std::string dateTimeRaw; // "DDMMYYhhmm"
|
|
58
|
+
int status = -1; // 0..6, -1 = unknown/missing
|
|
59
|
+
std::string softwareRelease;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
struct TotalsResponse {
|
|
63
|
+
Outcome outcome = Outcome::Unknown;
|
|
64
|
+
std::string resultCode;
|
|
65
|
+
std::string posTotal; // 16 digits, cents
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
struct CloseResponse {
|
|
69
|
+
Outcome outcome = Outcome::Unknown;
|
|
70
|
+
std::string resultCode;
|
|
71
|
+
std::string posTotal; // positive, 16 digits
|
|
72
|
+
std::string hostTotal; // positive, 16 digits
|
|
73
|
+
std::string errorDescription; // negative
|
|
74
|
+
std::string actionCode; // negative
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
struct PreAuthResponse {
|
|
78
|
+
Outcome outcome = Outcome::Unknown;
|
|
79
|
+
std::string resultCode;
|
|
80
|
+
std::string pan;
|
|
81
|
+
std::string transactionType;
|
|
82
|
+
std::string authCode;
|
|
83
|
+
std::string preAuthorizedAmount; // 8 digits, cents
|
|
84
|
+
std::string preAuthCode; // 9 digits, unique pre-auth id
|
|
85
|
+
std::string actionCode;
|
|
86
|
+
std::string hostDateTime;
|
|
87
|
+
std::string errorDescription;
|
|
88
|
+
std::string cardType;
|
|
89
|
+
std::string acquirerId;
|
|
90
|
+
std::string stan;
|
|
91
|
+
std::string onlineId;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
struct VasResponse {
|
|
95
|
+
std::string responseId; // RESPID parsed from XML ("0" = OK), "" if absent
|
|
96
|
+
std::string responseMessage; // RESPMSG
|
|
97
|
+
std::string orderId; // ORDER_ID
|
|
98
|
+
bool moreMessages = false; // concatenation flag "1"
|
|
99
|
+
std::string idMessage; // 3-digit sequence
|
|
100
|
+
std::string rawXml; // the XML body of this message
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
class Ecr17Response {
|
|
104
|
+
public:
|
|
105
|
+
static PaymentResponse parsePayment(const std::string& payload);
|
|
106
|
+
static StatusResponse parseStatus(const std::string& payload);
|
|
107
|
+
static TotalsResponse parseTotals(const std::string& payload);
|
|
108
|
+
static CloseResponse parseClose(const std::string& payload);
|
|
109
|
+
static PreAuthResponse parsePreAuth(const std::string& payload);
|
|
110
|
+
static VasResponse parseVas(const std::string& payload);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
} // namespace margelo::nitro::ecr17
|
package/cpp/Lcr/Lcr.cpp
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
#include "Lcr.hpp"
|
|
2
|
-
|
|
3
|
-
namespace margelo::nitro::ecr17 {
|
|
4
|
-
|
|
5
|
-
static constexpr uint8_t STX = 0x02;
|
|
6
|
-
static constexpr uint8_t ETX = 0x03;
|
|
7
|
-
|
|
8
|
-
uint8_t Lrc::compute(const std::vector<uint8_t>& payload, LrcMode mode) {
|
|
9
|
-
uint8_t lrc = BASE;
|
|
10
|
-
|
|
11
|
-
switch (mode) {
|
|
12
|
-
case LrcMode::STX:
|
|
13
|
-
case LrcMode::STX_NOEXT:
|
|
14
|
-
lrc ^= STX;
|
|
15
|
-
break;
|
|
16
|
-
|
|
17
|
-
default:
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
for (auto b : payload) {
|
|
22
|
-
lrc ^= b;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
switch (mode) {
|
|
26
|
-
case LrcMode::STX:
|
|
27
|
-
case LrcMode::NOEXT:
|
|
28
|
-
lrc ^= ETX;
|
|
29
|
-
break;
|
|
30
|
-
|
|
31
|
-
default:
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return lrc;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
uint8_t Lrc::compute(const std::string& payload, LrcMode mode) {
|
|
39
|
-
return compute(std::vector<uint8_t>(payload.begin(), payload.end()), mode);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
} // namespace margelo::nitro::ecr17
|
|
1
|
+
#include "Lcr.hpp"
|
|
2
|
+
|
|
3
|
+
namespace margelo::nitro::ecr17 {
|
|
4
|
+
|
|
5
|
+
static constexpr uint8_t STX = 0x02;
|
|
6
|
+
static constexpr uint8_t ETX = 0x03;
|
|
7
|
+
|
|
8
|
+
uint8_t Lrc::compute(const std::vector<uint8_t>& payload, LrcMode mode) {
|
|
9
|
+
uint8_t lrc = BASE;
|
|
10
|
+
|
|
11
|
+
switch (mode) {
|
|
12
|
+
case LrcMode::STX:
|
|
13
|
+
case LrcMode::STX_NOEXT:
|
|
14
|
+
lrc ^= STX;
|
|
15
|
+
break;
|
|
16
|
+
|
|
17
|
+
default:
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (auto b : payload) {
|
|
22
|
+
lrc ^= b;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
switch (mode) {
|
|
26
|
+
case LrcMode::STX:
|
|
27
|
+
case LrcMode::NOEXT:
|
|
28
|
+
lrc ^= ETX;
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return lrc;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
uint8_t Lrc::compute(const std::string& payload, LrcMode mode) {
|
|
39
|
+
return compute(std::vector<uint8_t>(payload.begin(), payload.end()), mode);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace margelo::nitro::ecr17
|
package/cpp/Lcr/Lcr.hpp
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
// cpp/core/Lrc.hpp
|
|
2
|
-
|
|
3
|
-
#pragma once
|
|
4
|
-
|
|
5
|
-
#include <cstdint>
|
|
6
|
-
#include <string>
|
|
7
|
-
#include <vector>
|
|
8
|
-
|
|
9
|
-
#include "LrcMode.hpp"
|
|
10
|
-
|
|
11
|
-
namespace margelo::nitro::ecr17 {
|
|
12
|
-
|
|
13
|
-
class Lrc {
|
|
14
|
-
public:
|
|
15
|
-
static constexpr uint8_t BASE = 0x7F;
|
|
16
|
-
|
|
17
|
-
static uint8_t compute(const std::vector<uint8_t>& payload, LrcMode mode);
|
|
18
|
-
|
|
19
|
-
static uint8_t compute(const std::string& payload, LrcMode mode);
|
|
20
|
-
};
|
|
21
|
-
|
|
1
|
+
// cpp/core/Lrc.hpp
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
#include "LrcMode.hpp"
|
|
10
|
+
|
|
11
|
+
namespace margelo::nitro::ecr17 {
|
|
12
|
+
|
|
13
|
+
class Lrc {
|
|
14
|
+
public:
|
|
15
|
+
static constexpr uint8_t BASE = 0x7F;
|
|
16
|
+
|
|
17
|
+
static uint8_t compute(const std::vector<uint8_t>& payload, LrcMode mode);
|
|
18
|
+
|
|
19
|
+
static uint8_t compute(const std::string& payload, LrcMode mode);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
22
|
} // namespace margelo::nitro::ecr17
|
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
#include "PacketCodec.hpp"
|
|
2
|
-
|
|
3
|
-
#include <algorithm> // std::find
|
|
4
|
-
#include <iterator> // std::distance
|
|
5
|
-
|
|
6
|
-
namespace margelo::nitro::ecr17 {
|
|
7
|
-
|
|
8
|
-
PacketCodec::PacketCodec(LrcMode mode) : lrcMode_(mode) {}
|
|
9
|
-
|
|
10
|
-
std::vector<uint8_t> PacketCodec::encodeApplication(const std::string& payload) {
|
|
11
|
-
std::vector<uint8_t> frame;
|
|
12
|
-
|
|
13
|
-
frame.push_back(STX);
|
|
14
|
-
|
|
15
|
-
frame.insert(frame.end(), payload.begin(), payload.end());
|
|
16
|
-
|
|
17
|
-
frame.push_back(ETX);
|
|
18
|
-
|
|
19
|
-
uint8_t lrc = Lrc::compute(payload, lrcMode_);
|
|
20
|
-
|
|
21
|
-
frame.push_back(lrc);
|
|
22
|
-
|
|
23
|
-
return frame;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
std::vector<uint8_t> PacketCodec::encodeControl(uint8_t ctrl) {
|
|
27
|
-
std::vector<uint8_t> frame;
|
|
28
|
-
|
|
29
|
-
frame.push_back(ctrl);
|
|
30
|
-
frame.push_back(ETX);
|
|
31
|
-
|
|
32
|
-
uint8_t lrc = Lrc::compute(std::vector<uint8_t>{ctrl}, lrcMode_);
|
|
33
|
-
|
|
34
|
-
frame.push_back(lrc);
|
|
35
|
-
|
|
36
|
-
return frame;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
DecodedPacket PacketCodec::decode(const std::vector<uint8_t>& data) {
|
|
40
|
-
if (data.empty()) {
|
|
41
|
-
return {
|
|
42
|
-
PacketType::UNKNOWN,
|
|
43
|
-
"",
|
|
44
|
-
false,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
uint8_t first = data[0];
|
|
49
|
-
|
|
50
|
-
if (first == ACK) {
|
|
51
|
-
return {
|
|
52
|
-
PacketType::ACK,
|
|
53
|
-
"",
|
|
54
|
-
true,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (first == NAK) {
|
|
59
|
-
return {
|
|
60
|
-
PacketType::NAK,
|
|
61
|
-
"",
|
|
62
|
-
true,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (first == SOH) {
|
|
67
|
-
// Progress update packet: SOH + message + EOT (no LRC). We need at least
|
|
68
|
-
// SOH and the trailing EOT before stripping the first/last byte,
|
|
69
|
-
// otherwise the iterator range below would be invalid (last < first).
|
|
70
|
-
if (data.size() < 2) {
|
|
71
|
-
return {
|
|
72
|
-
PacketType::UNKNOWN,
|
|
73
|
-
"",
|
|
74
|
-
false,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// The spec mandates SOH + 20-char message + EOT. Reject any frame whose
|
|
79
|
-
// final byte is not EOT so that garbage or a truncated read is never
|
|
80
|
-
// silently accepted as a valid progress update.
|
|
81
|
-
if (data.back() != EOT) {
|
|
82
|
-
return {
|
|
83
|
-
PacketType::UNKNOWN,
|
|
84
|
-
"",
|
|
85
|
-
false,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
std::string payload(data.begin() + 1, data.end() - 1);
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
PacketType::PROGRESS,
|
|
93
|
-
payload,
|
|
94
|
-
true,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (first == STX) {
|
|
99
|
-
auto etxIt = std::find(data.begin(), data.end(), ETX);
|
|
100
|
-
|
|
101
|
-
if (etxIt == data.end()) {
|
|
102
|
-
return {
|
|
103
|
-
PacketType::UNKNOWN,
|
|
104
|
-
"",
|
|
105
|
-
false,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
size_t etxIndex = std::distance(data.begin(), etxIt);
|
|
110
|
-
|
|
111
|
-
// A well-formed application frame is exactly STX + payload + ETX + LRC,
|
|
112
|
-
// so the LRC must be the final byte. Reject both a truncated frame (no
|
|
113
|
-
// LRC after ETX) and a buffer with trailing bytes -- e.g. a coalesced
|
|
114
|
-
// socket read holding a second frame ("STX..ETX LRC STX..") or garbage
|
|
115
|
-
// after the LRC. Splitting a byte stream into individual frames is the
|
|
116
|
-
// transport layer's responsibility, and DecodedPacket cannot carry the
|
|
117
|
-
// unconsumed remainder.
|
|
118
|
-
if (etxIndex + 2 != data.size()) {
|
|
119
|
-
return {
|
|
120
|
-
PacketType::UNKNOWN,
|
|
121
|
-
"",
|
|
122
|
-
false,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
std::string payload(data.begin() + 1, data.begin() + etxIndex);
|
|
127
|
-
|
|
128
|
-
uint8_t rxLrc = data[etxIndex + 1];
|
|
129
|
-
|
|
130
|
-
uint8_t calcLrc = Lrc::compute(payload, lrcMode_);
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
PacketType::APPLICATION,
|
|
134
|
-
payload,
|
|
135
|
-
rxLrc == calcLrc,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
PacketType::UNKNOWN,
|
|
141
|
-
"",
|
|
142
|
-
false,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
1
|
+
#include "PacketCodec.hpp"
|
|
2
|
+
|
|
3
|
+
#include <algorithm> // std::find
|
|
4
|
+
#include <iterator> // std::distance
|
|
5
|
+
|
|
6
|
+
namespace margelo::nitro::ecr17 {
|
|
7
|
+
|
|
8
|
+
PacketCodec::PacketCodec(LrcMode mode) : lrcMode_(mode) {}
|
|
9
|
+
|
|
10
|
+
std::vector<uint8_t> PacketCodec::encodeApplication(const std::string& payload) {
|
|
11
|
+
std::vector<uint8_t> frame;
|
|
12
|
+
|
|
13
|
+
frame.push_back(STX);
|
|
14
|
+
|
|
15
|
+
frame.insert(frame.end(), payload.begin(), payload.end());
|
|
16
|
+
|
|
17
|
+
frame.push_back(ETX);
|
|
18
|
+
|
|
19
|
+
uint8_t lrc = Lrc::compute(payload, lrcMode_);
|
|
20
|
+
|
|
21
|
+
frame.push_back(lrc);
|
|
22
|
+
|
|
23
|
+
return frame;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
std::vector<uint8_t> PacketCodec::encodeControl(uint8_t ctrl) {
|
|
27
|
+
std::vector<uint8_t> frame;
|
|
28
|
+
|
|
29
|
+
frame.push_back(ctrl);
|
|
30
|
+
frame.push_back(ETX);
|
|
31
|
+
|
|
32
|
+
uint8_t lrc = Lrc::compute(std::vector<uint8_t>{ctrl}, lrcMode_);
|
|
33
|
+
|
|
34
|
+
frame.push_back(lrc);
|
|
35
|
+
|
|
36
|
+
return frame;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
DecodedPacket PacketCodec::decode(const std::vector<uint8_t>& data) {
|
|
40
|
+
if (data.empty()) {
|
|
41
|
+
return {
|
|
42
|
+
PacketType::UNKNOWN,
|
|
43
|
+
"",
|
|
44
|
+
false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
uint8_t first = data[0];
|
|
49
|
+
|
|
50
|
+
if (first == ACK) {
|
|
51
|
+
return {
|
|
52
|
+
PacketType::ACK,
|
|
53
|
+
"",
|
|
54
|
+
true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (first == NAK) {
|
|
59
|
+
return {
|
|
60
|
+
PacketType::NAK,
|
|
61
|
+
"",
|
|
62
|
+
true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (first == SOH) {
|
|
67
|
+
// Progress update packet: SOH + message + EOT (no LRC). We need at least
|
|
68
|
+
// SOH and the trailing EOT before stripping the first/last byte,
|
|
69
|
+
// otherwise the iterator range below would be invalid (last < first).
|
|
70
|
+
if (data.size() < 2) {
|
|
71
|
+
return {
|
|
72
|
+
PacketType::UNKNOWN,
|
|
73
|
+
"",
|
|
74
|
+
false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The spec mandates SOH + 20-char message + EOT. Reject any frame whose
|
|
79
|
+
// final byte is not EOT so that garbage or a truncated read is never
|
|
80
|
+
// silently accepted as a valid progress update.
|
|
81
|
+
if (data.back() != EOT) {
|
|
82
|
+
return {
|
|
83
|
+
PacketType::UNKNOWN,
|
|
84
|
+
"",
|
|
85
|
+
false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
std::string payload(data.begin() + 1, data.end() - 1);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
PacketType::PROGRESS,
|
|
93
|
+
payload,
|
|
94
|
+
true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (first == STX) {
|
|
99
|
+
auto etxIt = std::find(data.begin(), data.end(), ETX);
|
|
100
|
+
|
|
101
|
+
if (etxIt == data.end()) {
|
|
102
|
+
return {
|
|
103
|
+
PacketType::UNKNOWN,
|
|
104
|
+
"",
|
|
105
|
+
false,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
size_t etxIndex = std::distance(data.begin(), etxIt);
|
|
110
|
+
|
|
111
|
+
// A well-formed application frame is exactly STX + payload + ETX + LRC,
|
|
112
|
+
// so the LRC must be the final byte. Reject both a truncated frame (no
|
|
113
|
+
// LRC after ETX) and a buffer with trailing bytes -- e.g. a coalesced
|
|
114
|
+
// socket read holding a second frame ("STX..ETX LRC STX..") or garbage
|
|
115
|
+
// after the LRC. Splitting a byte stream into individual frames is the
|
|
116
|
+
// transport layer's responsibility, and DecodedPacket cannot carry the
|
|
117
|
+
// unconsumed remainder.
|
|
118
|
+
if (etxIndex + 2 != data.size()) {
|
|
119
|
+
return {
|
|
120
|
+
PacketType::UNKNOWN,
|
|
121
|
+
"",
|
|
122
|
+
false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
std::string payload(data.begin() + 1, data.begin() + etxIndex);
|
|
127
|
+
|
|
128
|
+
uint8_t rxLrc = data[etxIndex + 1];
|
|
129
|
+
|
|
130
|
+
uint8_t calcLrc = Lrc::compute(payload, lrcMode_);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
PacketType::APPLICATION,
|
|
134
|
+
payload,
|
|
135
|
+
rxLrc == calcLrc,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
PacketType::UNKNOWN,
|
|
141
|
+
"",
|
|
142
|
+
false,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
146
|
} // namespace margelo::nitro::ecr17
|