diff --git a/src/HeboTech.ATLib.TestConsole/HeboTech.ATLib.TestConsole.csproj b/src/HeboTech.ATLib.TestConsole/HeboTech.ATLib.TestConsole.csproj
index 04aa4d7..0e37a85 100644
--- a/src/HeboTech.ATLib.TestConsole/HeboTech.ATLib.TestConsole.csproj
+++ b/src/HeboTech.ATLib.TestConsole/HeboTech.ATLib.TestConsole.csproj
@@ -1,16 +1,20 @@
-
- Exe
- net6.0
-
+
+ Exe
+ net6.0;net48
+
-
-
-
+
+ 9.0
+
-
-
-
+
+
+
+
+
+
+
diff --git a/src/HeboTech.ATLib.TestConsole/Program.cs b/src/HeboTech.ATLib.TestConsole/Program.cs
index 05c7960..72f1ca8 100644
--- a/src/HeboTech.ATLib.TestConsole/Program.cs
+++ b/src/HeboTech.ATLib.TestConsole/Program.cs
@@ -1,7 +1,10 @@
using System;
using System.IO;
using System.IO.Ports;
+using System.Linq;
using System.Net.Sockets;
+using System.Reflection;
+using System.Runtime.Versioning;
using System.Threading.Tasks;
namespace HeboTech.ATLib.TestConsole
@@ -10,6 +13,14 @@ class Program
{
static async Task Main(string[] args)
{
+ // Because of multi targeting, print out current framework target for information
+ var targetFrameworkAttribute = Assembly.GetExecutingAssembly()
+ .GetCustomAttributes(typeof(TargetFrameworkAttribute), false)
+ .SingleOrDefault() as TargetFrameworkAttribute;
+ Console.WriteLine($"Current target: {targetFrameworkAttribute.FrameworkName}");
+
+
+
Console.OutputEncoding = System.Text.Encoding.UTF8;
string pin = args[0];
diff --git a/src/HeboTech.ATLib.Tests/HeboTech.ATLib.Tests.csproj b/src/HeboTech.ATLib.Tests/HeboTech.ATLib.Tests.csproj
index 7692637..f41927f 100644
--- a/src/HeboTech.ATLib.Tests/HeboTech.ATLib.Tests.csproj
+++ b/src/HeboTech.ATLib.Tests/HeboTech.ATLib.Tests.csproj
@@ -1,23 +1,26 @@
-
+
-
- net6.0
+
+ net6.0;net48
+ false
+
- false
-
+
+ 9.0
+
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
-
-
-
+
+
+
diff --git a/src/HeboTech.ATLib.Tests/PDU/PduTests.cs b/src/HeboTech.ATLib.Tests/PDU/PduTests.cs
index 4ab5c8c..14d2691 100644
--- a/src/HeboTech.ATLib.Tests/PDU/PduTests.cs
+++ b/src/HeboTech.ATLib.Tests/PDU/PduTests.cs
@@ -1,6 +1,7 @@
using HeboTech.ATLib.CodingSchemes;
using HeboTech.ATLib.DTOs;
using HeboTech.ATLib.PDU;
+using System;
using Xunit;
namespace HeboTech.ATLib.Tests.PDU
@@ -23,7 +24,11 @@ public void Encode_SmsSubmit_test(string phoneNumber, string encodedMessage, byt
[InlineData("07911326040000F0040B911346610089F60000208062917314800CC8F71D14969741F977FD07", "31624000000", "31641600986", "02-08-26-19-37-41-+02", "How are you?")]
public void Decode_SmsDeliver_tests(string data, string serviceCenterNumber, string senderNumber, string timestamp, string message)
{
+#if NETFRAMEWORK
+ SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(data.AsSpan());
+#else
SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(data);
+#endif
Assert.NotNull(pduMessage);
Assert.Equal(TypeOfNumber.International, pduMessage.ServiceCenterNumber.Ton);
@@ -39,7 +44,11 @@ public void Decode_SmsDeliver_tests(string data, string serviceCenterNumber, str
[InlineData("0011000802231537180000AA0D5062154403D1CB68D03DED06", "", "32517381", "PDU 4 teh win")]
public void Decode_SmsSubmit_tests(string data, string serviceCenterNumber, string senderNumber, string message)
{
+#if NETFRAMEWORK
+ SmsSubmit pduMessage = Pdu.DecodeSmsSubmit(data.AsSpan());
+#else
SmsSubmit pduMessage = Pdu.DecodeSmsSubmit(data);
+#endif
Assert.NotNull(pduMessage);
Assert.Equal(serviceCenterNumber, pduMessage.ServiceCenterNumber?.Number ?? "");
diff --git a/src/HeboTech.ATLib.Tests/Parsers/AtReaderTests.cs b/src/HeboTech.ATLib.Tests/Parsers/AtReaderTests.cs
index 229735f..322541a 100644
--- a/src/HeboTech.ATLib.Tests/Parsers/AtReaderTests.cs
+++ b/src/HeboTech.ATLib.Tests/Parsers/AtReaderTests.cs
@@ -16,7 +16,11 @@ public async Task Lines_are_readAsync()
string input = "Line1\r\nLine2\r\nLine3\r\n";
byte[] buffer = Encoding.UTF8.GetBytes(input);
+#if NETFRAMEWORK
+ stream.Write(buffer, 0, buffer.Length);
+#else
stream.Write(buffer);
+#endif
stream.Position = 0;
dut.Open();
@@ -40,7 +44,11 @@ public async Task Lines_and_sms_prompts_are_readAsync()
string input = "Line1\r\nLine2\r\n> Line3\r\n";
byte[] buffer = Encoding.UTF8.GetBytes(input);
+#if NETFRAMEWORK
+ stream.Write(buffer, 0, buffer.Length);
+#else
stream.Write(buffer);
+#endif
stream.Position = 0;
dut.Open();
@@ -66,7 +74,11 @@ public async Task Empty_lines_are_readAsync()
string input = "\r\n\r\n\r\n";
byte[] buffer = Encoding.UTF8.GetBytes(input);
+#if NETFRAMEWORK
+ stream.Write(buffer, 0, buffer.Length);
+#else
stream.Write(buffer);
+#endif
stream.Position = 0;
dut.Open();
@@ -90,7 +102,11 @@ public async Task Cme_Error_is_readAsync()
string input = "+CME ERROR: ErrorMessage\r\n";
byte[] buffer = Encoding.UTF8.GetBytes(input);
+#if NETFRAMEWORK
+ stream.Write(buffer, 0, buffer.Length);
+#else
stream.Write(buffer);
+#endif
stream.Position = 0;
dut.Open();
@@ -110,7 +126,11 @@ public async Task Ring_is_readAsync()
string input = "RING\r\n\r\nRING\r\n\r\nMISSED_CALL: 01:23PM 12345678\r\n";
byte[] buffer = Encoding.UTF8.GetBytes(input);
+#if NETFRAMEWORK
+ stream.Write(buffer, 0, buffer.Length);
+#else
stream.Write(buffer);
+#endif
stream.Position = 0;
dut.Open();
diff --git a/src/HeboTech.ATLib.sln b/src/HeboTech.ATLib.sln
index b4d5034..4589cc3 100644
--- a/src/HeboTech.ATLib.sln
+++ b/src/HeboTech.ATLib.sln
@@ -1,13 +1,13 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30011.22
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeboTech.ATLib", "HeboTech.ATLib\HeboTech.ATLib.csproj", "{F919890A-9835-4D57-80F1-8F6BF2CC2085}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeboTech.ATLib.TestConsole", "HeboTech.ATLib.TestConsole\HeboTech.ATLib.TestConsole.csproj", "{82A5A7D4-9AD3-4B95-AD72-CD1B48017AC3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeboTech.ATLib.Tests", "HeboTech.ATLib.Tests\HeboTech.ATLib.Tests.csproj", "{97A95146-06D3-436E-AE16-8F0A6D86B26D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeboTech.ATLib.Tests", "HeboTech.ATLib.Tests\HeboTech.ATLib.Tests.csproj", "{97A95146-06D3-436E-AE16-8F0A6D86B26D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/HeboTech.ATLib/DTOs/PhoneNumber.cs b/src/HeboTech.ATLib/DTOs/PhoneNumber.cs
index 6fe4917..3f388e2 100644
--- a/src/HeboTech.ATLib/DTOs/PhoneNumber.cs
+++ b/src/HeboTech.ATLib/DTOs/PhoneNumber.cs
@@ -4,7 +4,11 @@ public class PhoneNumber
{
public PhoneNumber(string number)
{
+#if NETSTANDARD2_0
+ if (number.StartsWith("+"))
+#else
if (number.StartsWith('+'))
+#endif
{
Ton = TypeOfNumber.International;
Npi = NumberPlanIdentification.ISDN;
diff --git a/src/HeboTech.ATLib/DTOs/SupportedPreferredMessageStorages.cs b/src/HeboTech.ATLib/DTOs/SupportedPreferredMessageStorages.cs
index 2b11c40..39a921a 100644
--- a/src/HeboTech.ATLib/DTOs/SupportedPreferredMessageStorages.cs
+++ b/src/HeboTech.ATLib/DTOs/SupportedPreferredMessageStorages.cs
@@ -18,10 +18,17 @@ public SupportedPreferredMessageStorages(IEnumerable storage1, IEnumerab
public override string ToString()
{
+#if NETSTANDARD2_0
+ return
+ $"Storage1: {string.Join(",", Storage1)}{Environment.NewLine}" +
+ $"Storage2: {string.Join(",", Storage2)}{Environment.NewLine}" +
+ $"Storage3: {string.Join(",", Storage3)}";
+#elif NETSTANDARD2_1_OR_GREATER
return
$"Storage1: {string.Join(',', Storage1)}{Environment.NewLine}" +
$"Storage2: {string.Join(',', Storage2)}{Environment.NewLine}" +
$"Storage3: {string.Join(',', Storage3)}";
+#endif
}
}
}
diff --git a/src/HeboTech.ATLib/Events/MissedCallEventArgs.cs b/src/HeboTech.ATLib/Events/MissedCallEventArgs.cs
index 380a478..64b76a3 100644
--- a/src/HeboTech.ATLib/Events/MissedCallEventArgs.cs
+++ b/src/HeboTech.ATLib/Events/MissedCallEventArgs.cs
@@ -13,7 +13,11 @@ public MissedCallEventArgs(string time, string phoneNumber)
public static MissedCallEventArgs CreateFromResponse(string response)
{
+#if NETSTANDARD2_0
+ string[] split = response.Split(new char[] { ' ' }, 3);
+#elif NETSTANDARD2_1_OR_GREATER
string[] split = response.Split(' ', 3);
+#endif
return new MissedCallEventArgs(split[1], split[2]);
}
}
diff --git a/src/HeboTech.ATLib/HeboTech.ATLib.csproj b/src/HeboTech.ATLib/HeboTech.ATLib.csproj
index 7422255..a2b160d 100644
--- a/src/HeboTech.ATLib/HeboTech.ATLib.csproj
+++ b/src/HeboTech.ATLib/HeboTech.ATLib.csproj
@@ -1,7 +1,7 @@
- netstandard2.1
+ netstandard2.1;netstandard2.0
HeboTech
HeboTech ATLib
4.1.0
@@ -15,12 +15,16 @@
https://github.com/hbjorgo/ATLib/releases
4.1.0.0
4.1.0.0
+ b8328b1a-795d-4e26-9238-43eee2160ffc
-
-
+
+
+
+
+
diff --git a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs
index 4d6f685..254167e 100644
--- a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs
+++ b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs
@@ -122,7 +122,11 @@ public virtual async Task>> GetAvailableCharac
var match = Regex.Match(line, @"\+CSCS:\s\((?:""(?\w+)"",*)+\)");
if (match.Success)
{
+#if NETSTANDARD2_0
+ return ModemResponse.ResultSuccess(match.Groups["characterSet"].Captures.Cast().Select(x => x.Value));
+#elif NETSTANDARD2_1_OR_GREATER
return ModemResponse.ResultSuccess(match.Groups["characterSet"].Captures.Select(x => x.Value));
+#endif
}
}
return ModemResponse.ResultError>();
@@ -150,9 +154,9 @@ public virtual async Task SetCharacterSetAsync(string characterSe
AtResponse response = await channel.SendCommand($"AT+CSCS=\"{characterSet}\"");
return ModemResponse.Success(response.Success);
}
- #endregion
+#endregion
- #region _3GPP_TS_27_005
+#region _3GPP_TS_27_005
public event EventHandler SmsReceived;
public virtual async Task SetSmsMessageFormatAsync(SmsTextFormat format)
@@ -317,7 +321,11 @@ public virtual async Task> ReadSmsAsync(int index, SmsTextFor
SmsStatus status = SmsStatusHelpers.ToSmsStatus(statusCode);
string pdu = line2Match.Groups["status"].Value;
+#if NETSTANDARD2_0
+ SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(pdu.AsSpan());
+#elif NETSTANDARD2_1_OR_GREATER
SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(pdu);
+#endif
return ModemResponse.ResultSuccess(new Sms(status, pduMessage.SenderNumber, pduMessage.Timestamp, pduMessage.Message));
}
@@ -408,9 +416,9 @@ public virtual async Task DeleteSmsAsync(int index)
AtResponse response = await channel.SendCommand($"AT+CMGD={index}");
return ModemResponse.Success(response.Success);
}
- #endregion
+#endregion
- #region _3GPP_TS_27_007
+#region _3GPP_TS_27_007
public event EventHandler UssdResponseReceived;
public virtual async Task> GetSimStatusAsync()
@@ -429,6 +437,16 @@ public virtual async Task> GetSimStatusAsync()
if (match.Success)
{
string cpinResult = match.Groups["pinresult"].Value;
+#if NETSTANDARD2_0
+ switch(cpinResult)
+ {
+ case "SIM PIN": return ModemResponse.ResultSuccess(SimStatus.SIM_PIN);
+ case "SIM PUK": return ModemResponse.ResultSuccess(SimStatus.SIM_PUK);
+ case "PH-NET PIN": return ModemResponse.ResultSuccess(SimStatus.SIM_NETWORK_PERSONALIZATION);
+ case "READY": return ModemResponse.ResultSuccess(SimStatus.SIM_READY);
+ default: return ModemResponse.ResultSuccess(SimStatus.SIM_ABSENT);// Treat unsupported lock types as "sim absent"
+ };
+#elif NETSTANDARD2_1_OR_GREATER
return cpinResult switch
{
"SIM PIN" => ModemResponse.ResultSuccess(SimStatus.SIM_PIN),
@@ -437,6 +455,7 @@ public virtual async Task> GetSimStatusAsync()
"READY" => ModemResponse.ResultSuccess(SimStatus.SIM_READY),
_ => ModemResponse.ResultSuccess(SimStatus.SIM_ABSENT),// Treat unsupported lock types as "sim absent"
};
+#endif
}
return ModemResponse.ResultError();
@@ -530,7 +549,7 @@ public virtual async Task SendUssdAsync(string code, int codingSc
AtResponse response = await channel.SendCommand($"AT+CUSD=1,\"{code}\",{codingScheme}");
return ModemResponse.Success(response.Success);
}
- #endregion
+#endregion
public virtual async Task SetErrorFormat(int errorFormat)
{
@@ -543,7 +562,7 @@ public void Close()
Dispose();
}
- #region Dispose
+#region Dispose
protected virtual void Dispose(bool disposing)
{
if (!disposed)
@@ -573,6 +592,6 @@ public void Dispose()
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
- #endregion
+#endregion
}
}
diff --git a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs
index fca216a..80334be 100644
--- a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs
+++ b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs
@@ -65,7 +65,11 @@ public override async Task> ReadSmsAsync(int index, SmsTextFo
string alphabet = line1Match.Groups["alphabet"].Value;
int length = int.Parse(line1Match.Groups["length"].Value);
string pdu = line2Match.Groups["pdu"].Value;
+#if NETSTANDARD2_0
+ SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(pdu.AsSpan());
+#elif NETSTANDARD2_1_OR_GREATER
SmsDeliver pduMessage = Pdu.DecodeSmsDeliver(pdu);
+#endif
return ModemResponse.ResultSuccess(new Sms((SmsStatus)status, pduMessage.SenderNumber, pduMessage.Timestamp, pduMessage.Message));
}
}
@@ -130,6 +134,6 @@ public override async Task>> ListSmssAsync(SmsS
}
return ModemResponse.ResultSuccess(smss);
}
- #endregion
+#endregion
}
}
diff --git a/src/HeboTech.ATLib/PDU/Pdu.cs b/src/HeboTech.ATLib/PDU/Pdu.cs
index 43c178b..2f45799 100644
--- a/src/HeboTech.ATLib/PDU/Pdu.cs
+++ b/src/HeboTech.ATLib/PDU/Pdu.cs
@@ -6,6 +6,227 @@
namespace HeboTech.ATLib.PDU
{
+#if NETSTANDARD2_0
+ public class Pdu
+ {
+ public static string EncodeSmsSubmit(PhoneNumber phoneNumber, string encodedMessage, byte dataCodingScheme, bool includeEmptySmscLength = true)
+ {
+ StringBuilder sb = new StringBuilder();
+ // Length of SMSC information
+ if (includeEmptySmscLength)
+ sb.Append("00");
+ // First octed of the SMS-SUBMIT message
+ sb.Append("11");
+ // TP-Message-Reference. '00' lets the phone set the message reference number itself
+ sb.Append("00");
+ // Address length. Length of phone number (number of digits)
+ sb.Append((phoneNumber.ToString().Length).ToString("X2"));
+ // Type-of-Address
+ sb.Append(GetAddressType(phoneNumber).ToString("X2"));
+ // Phone number in semi octets. 12345678 is represented as 21436587
+ sb.Append(SwapPhoneNumberDigits(phoneNumber.ToString()));
+ // TP-PID Protocol identifier
+ sb.Append("00");
+ // TP-DCS Data Coding Scheme. '00'-7bit default alphabet. '04'-8bit
+ sb.Append((dataCodingScheme).ToString("X2"));
+ // TP-Validity-Period. 'AA'-4 days
+ sb.Append("AA");
+ // TP-User-Data-Length. If TP-DCS field indicates 7-bit data, the length is the number of septets.
+ // If TP-DCS indicates 8-bit data or Unicode, the length is the number of octets.
+ if (dataCodingScheme == 0)
+ {
+ int messageBitLength = encodedMessage.Length / 2 * 7;
+ int messageLength = messageBitLength % 8 == 0 ? messageBitLength / 8 : (messageBitLength / 8) + 1;
+ sb.Append((messageLength).ToString("X2"));
+ }
+ else
+ sb.Append((encodedMessage.Length / 2 * 8 / 7).ToString("X2"));
+ sb.Append(encodedMessage);
+
+ return sb.ToString();
+ }
+
+ public static SmsDeliver DecodeSmsDeliver(ReadOnlySpan text, int timestampYearOffset = 2000)
+ {
+ int offset = 0;
+
+ // SMSC information
+ byte smsc_length = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ PhoneNumber serviceCenterNumber = null;
+ if (smsc_length > 0)
+ {
+ serviceCenterNumber = DecodePhoneNumber(text.SliceOnIndex(offset,(offset += smsc_length * 2)));
+ }
+
+ // SMS-DELIVER start
+ byte header = HexToByte(text.SliceOnIndex(offset,(offset += 2)));
+
+ int tp_mti = header & 0b0000_0011;
+ if (tp_mti != (byte)PduType.SMS_DELIVER)
+ throw new ArgumentException("Invalid SMS-DELIVER data");
+
+ int tp_mms = header & 0b0000_0100;
+ int tp_rp = header & 0b1000_0000;
+
+ byte tp_oa_length = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ tp_oa_length = (byte)(tp_oa_length % 2 == 0 ? tp_oa_length : tp_oa_length + 1);
+ PhoneNumber oa = null;
+ if (tp_oa_length > 0)
+ {
+ int oa_digits = tp_oa_length + 2; // Add 2 for TON
+ oa = DecodePhoneNumber(text.SliceOnIndex(offset, (offset += oa_digits)));
+ }
+ byte tp_pid = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ byte tp_dcs = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ ReadOnlySpan tp_scts = text.SliceOnIndex(offset, (offset += 14));
+ byte tp_udl = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ int udlBytes = (int)Math.Ceiling(tp_udl * 7 / 8.0);
+
+ ReadOnlySpan tp_ud = text.SliceOnIndex(offset, (offset += ((udlBytes) * 2)));
+ string message = null;
+ switch (tp_dcs)
+ {
+ case 0x00:
+ message = Gsm7.Decode(tp_ud.ToString());
+ break;
+ default:
+ break;
+ }
+ DateTimeOffset scts = DecodeTimestamp(tp_scts, timestampYearOffset);
+ return new SmsDeliver(serviceCenterNumber, oa, message, scts);
+ }
+
+ public static SmsSubmit DecodeSmsSubmit(ReadOnlySpan text, int timestampYearOffset = 2000)
+ {
+ int offset = 0;
+
+ // SMSC information
+ byte smsc_length = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ PhoneNumber serviceCenterNumber = null;
+ if (smsc_length > 0)
+ {
+ serviceCenterNumber = DecodePhoneNumber(text.SliceOnIndex(offset, (offset += smsc_length * 2)));
+ }
+
+ // SMS-DELIVER start
+ byte header = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+
+ int tp_mti = header & 0b0000_0011;
+ if (tp_mti != (byte)PduType.SMS_SUBMIT)
+ throw new ArgumentException("Invalid SMS-SUBMIT data");
+
+ int tp_rd = header & 0b0000_0100;
+ int tp_vpf = header & 0b0001_1000;
+ int tp_rp = header & 0b1000_0000;
+
+ byte tp_mr = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ byte tp_oa_length = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ tp_oa_length = (byte)(tp_oa_length % 2 == 0 ? tp_oa_length : tp_oa_length + 1);
+ PhoneNumber oa = null;
+ if (tp_oa_length > 0)
+ {
+ int oa_digits = tp_oa_length + 2; // Add 2 for TON
+ oa = DecodePhoneNumber(text.SliceOnIndex(offset, (offset += oa_digits)));
+ }
+ byte tp_pid = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ byte tp_dcs = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ byte tp_vp = 0;
+ if (tp_vpf == 0x00)
+ tp_vp = HexToByte(text.SliceOnIndex(offset, (offset += 0)));
+ else if (tp_vpf == 0x01)
+ tp_vp = HexToByte(text.SliceOnIndex(offset, (offset += 14)));
+ else if (tp_vpf == 0x10)
+ tp_vp = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+ else if (tp_vpf == 0x11)
+ tp_vp = HexToByte(text.SliceOnIndex(offset, (offset += 14)));
+ byte tp_udl = HexToByte(text.SliceOnIndex(offset, (offset += 2)));
+
+ string message = null;
+ switch (tp_dcs)
+ {
+ case 0x00:
+ int length = (tp_udl * 7 / 8) + 1;
+ ReadOnlySpan tp_ud = text.SliceOnIndex(offset, (offset += ((length) * 2)));
+ message = Gsm7.Decode(tp_ud.ToString());
+ break;
+ default:
+ break;
+ }
+ return new SmsSubmit(serviceCenterNumber, oa, message);
+ }
+
+ private static byte HexToByte(ReadOnlySpan text)
+ {
+ byte retVal = (byte)int.Parse(text.ToString(), NumberStyles.HexNumber);
+ return retVal;
+ }
+
+ private static char[] SwapPhoneNumberDigits(string data)
+ {
+ char[] swappedData = new char[data.Length];
+ for (int i = 0; i < data.Length; i += 2)
+ {
+ swappedData[i] = data[i + 1];
+ swappedData[i + 1] = data[i];
+ }
+ if (swappedData[swappedData.Length - 1] == 'F')
+ {
+ char[] subArray = new char[swappedData.Length - 1];
+ Array.Copy(swappedData, subArray, subArray.Length);
+ return subArray;
+ }
+ return swappedData;
+ }
+
+ private static byte GetAddressType(PhoneNumber phoneNumber)
+ {
+ return (byte)(0b1000_0000 + (byte)phoneNumber.Ton + (byte)phoneNumber.Npi);
+ }
+
+ private static PhoneNumber DecodePhoneNumber(ReadOnlySpan data)
+ {
+ if (data.Length < 4)
+ return default;
+ TypeOfNumber ton = (TypeOfNumber)((HexToByte(data.Slice(0, 2)) & 0b0111_0000) >> 4);
+ string number = new String(SwapPhoneNumberDigits(data.Slice(2).ToString()));
+ return new PhoneNumber(
+ number,
+ ton,
+ ton == TypeOfNumber.International ? NumberPlanIdentification.ISDN : NumberPlanIdentification.Unknown);
+ }
+
+ private static DateTimeOffset DecodeTimestamp(ReadOnlySpan data, int timestampYearOffset = 2000)
+ {
+ char[] swappedData = new char[data.Length];
+ for (int i = 0; i < swappedData.Length; i += 2)
+ {
+ swappedData[i] = data[i + 1];
+ swappedData[i + 1] = data[i];
+ }
+ ReadOnlySpan swappedSpan = swappedData;
+
+ byte offset = DecimalToByte(swappedSpan.SliceOnIndex(12, 14));
+ bool positive = (offset & (1 << 7)) == 0;
+ byte offsetQuarters = (byte)(offset & 0b0111_1111);
+
+ DateTimeOffset timestamp = new DateTimeOffset(
+ DecimalToByte(swappedSpan.SliceOnIndex(0, 2)) + timestampYearOffset,
+ DecimalToByte(swappedSpan.SliceOnIndex(2, 4)),
+ DecimalToByte(swappedSpan.SliceOnIndex(4, 6)),
+ DecimalToByte(swappedSpan.SliceOnIndex(6, 8)),
+ DecimalToByte(swappedSpan.SliceOnIndex(8, 10)),
+ DecimalToByte(swappedSpan.SliceOnIndex(10, 12)),
+ TimeSpan.FromMinutes(offsetQuarters * 15)); // Offset in quarter of hours
+ return timestamp;
+ }
+
+ private static byte DecimalToByte(ReadOnlySpan text)
+ {
+ return (byte)int.Parse(text.ToString(), NumberStyles.Integer);
+ }
+ }
+
+#elif NETSTANDARD2_1_OR_GREATER
public class Pdu
{
public static string EncodeSmsSubmit(PhoneNumber phoneNumber, string encodedMessage, byte dataCodingScheme, bool includeEmptySmscLength = true)
@@ -220,4 +441,5 @@ static byte DecimalToByte(ReadOnlySpan text)
}
}
}
+#endif
}
diff --git a/src/HeboTech.ATLib/PDU/ReadOnlySpanExtensions.cs b/src/HeboTech.ATLib/PDU/ReadOnlySpanExtensions.cs
new file mode 100644
index 0000000..7354ef4
--- /dev/null
+++ b/src/HeboTech.ATLib/PDU/ReadOnlySpanExtensions.cs
@@ -0,0 +1,14 @@
+#if NETSTANDARD2_0
+using System;
+
+namespace HeboTech.ATLib.PDU
+{
+ internal static class ReadOnlySpanExtensions
+ {
+ public static ReadOnlySpan SliceOnIndex(this ReadOnlySpan span, int start, int end)
+ {
+ return span.Slice(start, end - start);
+ }
+ }
+}
+#endif