From 74c58678201de7af514d6252439cb8f41e1e65ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Wed, 31 Jan 2024 14:59:03 +0100 Subject: [PATCH 1/6] Working sms status report --- .../FunctionalityTest.cs | 41 +++++++--- src/HeboTech.ATLib.TestConsole/Program.cs | 2 +- src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs | 52 +++++++++++++ src/HeboTech.ATLib/DTOs/SmsStatusReport.cs | 27 +++++++ .../Events/SmsStatusReportEventArgs.cs | 30 ++++++++ .../Modems/Generic/ModemBase.cs | 4 + src/HeboTech.ATLib/Modems/IModem.cs | 3 + .../PDU/MessageTypeIndicator.cs | 14 +++- src/HeboTech.ATLib/PDU/PhoneNumberDecoder.cs | 48 ++++++++++++ src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs | 43 +---------- .../PDU/SmsStatusReportDecoder.cs | 74 +++++++++++++++++++ src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs | 1 + 12 files changed, 283 insertions(+), 56 deletions(-) create mode 100644 src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs create mode 100644 src/HeboTech.ATLib/DTOs/SmsStatusReport.cs create mode 100644 src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs create mode 100644 src/HeboTech.ATLib/PDU/PhoneNumberDecoder.cs create mode 100644 src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs diff --git a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs index 8959fee..d172e1b 100644 --- a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs +++ b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs @@ -1,6 +1,7 @@ using HeboTech.ATLib.DTOs; using HeboTech.ATLib.Events; using HeboTech.ATLib.Modems.Cinterion; +using HeboTech.ATLib.Modems.D_LINK; using HeboTech.ATLib.Modems.Generic; using HeboTech.ATLib.Parsers; using System; @@ -16,7 +17,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) { using AtChannel atChannel = AtChannel.Create(stream); //atChannel.EnableDebug((string line) => Console.WriteLine(line)); - using IMC55i modem = new MC55i(atChannel); + using IDWM222 modem = new DWM222(atChannel); atChannel.Open(); await atChannel.ClearAsync(); @@ -28,6 +29,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) modem.UssdResponseReceived += Modem_UssdResponseReceived; modem.ErrorReceived += Modem_ErrorReceived; modem.GenericEvent += Modem_GenericEvent; + modem.SmsStatusReportReceived += Modem_SmsStatusReportReceived; // Configure modem with required settings before PIN var requiredSettingsBeforePin = await modem.SetRequiredSettingsBeforePinAsync(); @@ -99,7 +101,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) Console.WriteLine($"Date and time: {dateTime}"); - var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 0, 1); + var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 1, 0); Console.WriteLine($"Setting new SMS indication: {newSmsIndicationResult}"); var supportedStorages = await modem.GetSupportedPreferredMessageStoragesAsync(); @@ -109,7 +111,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) var setPreferredStorages = await modem.SetPreferredMessageStorageAsync(MessageStorage.SM, MessageStorage.SM, MessageStorage.SM); Console.WriteLine($"Storages set:{Environment.NewLine}{setPreferredStorages}"); - Console.WriteLine("Done. Press 'a' to answer call, 'd' to dial, 'h' to hang up, 's' to send SMS, 'r' to read an SMS, 'l' to list all SMSs, 'u' to send USSD code, 'x' to send raw command, 'z' to send raw command with response, '+' to enable debug, '-' to disable debug and 'q' to exit..."); + Console.WriteLine("Done. Press 'a' to answer call, 'd' to dial, 'h' to hang up, 's' to send SMS, 'r' to read an SMS, 'l' to list all SMSs, 'p' to delete an SMS, 'u' to send USSD code, 'x' to send raw command, 'z' to send raw command with response, '+' to enable debug, '-' to disable debug and 'q' to exit..."); ConsoleKey key; while ((key = Console.ReadKey().Key) != ConsoleKey.Q) { @@ -170,15 +172,29 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) break; } case ConsoleKey.R: - Console.WriteLine("Enter SMS index:"); - if (int.TryParse(Console.ReadLine(), out int smsIndex)) { - var sms = await modem.ReadSmsAsync(smsIndex); - Console.WriteLine(sms); + Console.WriteLine("Enter SMS index:"); + if (int.TryParse(Console.ReadLine(), out int smsIndex)) + { + var sms = await modem.ReadSmsAsync(smsIndex); + Console.WriteLine(sms); + } + else + Console.WriteLine("Invalid SMS index"); + break; + } + case ConsoleKey.P: + { + Console.WriteLine("Enter SMS index:"); + if (int.TryParse(Console.ReadLine(), out int smsIndex)) + { + var deleteResponse = await modem.DeleteSmsAsync(smsIndex); + Console.WriteLine(deleteResponse); + } + else + Console.WriteLine("Invalid SMS index"); + break; } - else - Console.WriteLine("Invalid SMS index"); - break; case ConsoleKey.U: Console.WriteLine("Enter USSD Code:"); var ussd = Console.ReadLine(); @@ -208,6 +224,11 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) } } + private static void Modem_SmsStatusReportReceived(object sender, SmsStatusReportEventArgs e) + { + Console.WriteLine($"SMS Status Report: {e.SmsStatusReport}"); + } + private static void Modem_GenericEvent(object sender, GenericEventArgs e) { Console.WriteLine($"Generic event: {e.Message}"); diff --git a/src/HeboTech.ATLib.TestConsole/Program.cs b/src/HeboTech.ATLib.TestConsole/Program.cs index 38e515e..305bc99 100644 --- a/src/HeboTech.ATLib.TestConsole/Program.cs +++ b/src/HeboTech.ATLib.TestConsole/Program.cs @@ -46,7 +46,7 @@ static async Task Main(string[] args) /* ######## UNCOMMENT THIS SECTION TO USE NETWORK SOCKET ######## */ using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - socket.Connect("192.168.1.144", 7000); + socket.Connect("192.168.1.5", 7000); Console.WriteLine("Network socket opened"); Stream stream; stream = new NetworkStream(socket); diff --git a/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs b/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs new file mode 100644 index 0000000..4dc2a84 --- /dev/null +++ b/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs @@ -0,0 +1,52 @@ +namespace HeboTech.ATLib.DTOs +{ + /// + /// Bits 0..6. Bit 7 is reserved. + /// + public enum SmsDeliveryStatus : byte + { + Message_received_by_SME = 0b0000_0000, + Forwarded_to_SME_but_unconfirmed_delivery = 0b0000_0001, + Message_replaced_by_SC = 0b0000_0010, + + // 000_0011..000_1111 Reserved + // 001_0000..0011111 Values specific to each SC + + // Temporary error, SC still trying to transfer to SM + Congestion_1 = 0b0010_0000, + SME_busy_1 = 0b0010_0001, + Service_rejected_1 = 0b0010_0011, + Quality_of_service_not_available_1 = 0b0010_0100, + Error_in_SME_1 = 0b0010_0101, + + // 0100110..0101111 Reserved + // 0110000..0111111 Values specific to each SC + + // Permanent error, SC is not making any more transfer attempts + Remote_procedure_error = 0b0100_0000, + Incompatible_destination = 0b0100_0001, + Connection_rejected_by_SME = 0b0100_0010, + Not_obtainable = 0b0100_0011, + Quality_of_service_not_available_2 = 0b0100_0100, + No_interworking_available = 0b0100_0101, + SM_validity_period_expired = 0b0100_0110, + SM_deleted_by_originating_SME = 0b0100_0111, + SM_deleted_by_SC_administration = 0b0100_1000, + SM_does_not_exist = 0b0100_1001, + + // 1001010..1001111 Reserved + // 1010000..1011111 Values specific to each SC + + // Temporary error, SC is not making any more transfer attempts + Congestion_2 = 0b0110_0000, + SME_busy_2 = 0b0110_0001, + No_response_from_SME = 0b0110_0010, + Service_rejected_2 = 0b0110_0011, + Quality_of_service_not_available_3 = 0b0110_0100, + Error_in_SME_2 = 0b0110_0101, + + //1100110..1101001 Reserved + //1101010..1101111 Reserved + //1110000..1111111 Values specific to each SC + } +} diff --git a/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs new file mode 100644 index 0000000..01dc2ff --- /dev/null +++ b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs @@ -0,0 +1,27 @@ +using System; + +namespace HeboTech.ATLib.DTOs +{ + public class SmsStatusReport + { + public SmsStatusReport(int messageReference, PhoneNumberDTO recipientAddress, DateTimeOffset serviceCenterTimestamp, DateTimeOffset dischargeTime, SmsDeliveryStatus status) + { + MessageReference = messageReference; + RecipientAddress = recipientAddress; + ServiceCenterTimestamp = serviceCenterTimestamp; + DischargeTime = dischargeTime; + Status = status; + } + + public int MessageReference { get; } + public PhoneNumberDTO RecipientAddress { get; } + public DateTimeOffset ServiceCenterTimestamp { get; } + public DateTimeOffset DischargeTime { get; } + public SmsDeliveryStatus Status { get; } + + public override string ToString() + { + return $"SMS no. {MessageReference} delivered with status {Status}. RA: {RecipientAddress}. SCTS: {ServiceCenterTimestamp}. DT: {DischargeTime}."; + } + } +} diff --git a/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs b/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs new file mode 100644 index 0000000..80e25ec --- /dev/null +++ b/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs @@ -0,0 +1,30 @@ +using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.Extensions; +using HeboTech.ATLib.PDU; +using System.Text.RegularExpressions; + +namespace HeboTech.ATLib.Events +{ + public class SmsStatusReportEventArgs + { + public SmsStatusReportEventArgs(SmsStatusReport smsStatusReport) + { + SmsStatusReport = smsStatusReport; + } + + public SmsStatusReport SmsStatusReport { get; } + + public static SmsStatusReportEventArgs CreateFromResponse(string line1, string line2) + { + var line1Match = Regex.Match(line1, @"\+CDS:\s(?\d+)"); + if (line1Match.Success) + { + byte length = byte.Parse(line1Match.Groups["length"].Value); + var report = SmsStatusReportDecoder.Decode(length, line2.ToByteArray()); + return new SmsStatusReportEventArgs(report); + } + + return default; + } + } +} diff --git a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs index aa4b286..45a1126 100644 --- a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs +++ b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs @@ -40,6 +40,8 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) SmsReceived?.Invoke(this, SmsReceivedEventArgs.CreateFromResponse(e.Line1)); else if (e.Line1.StartsWith("+CUSD: ")) UssdResponseReceived?.Invoke(this, UssdResponseEventArgs.CreateFromResponse(e.Line1)); + else if (e.Line1.StartsWith("+CDS: ")) + SmsStatusReportReceived?.Invoke(this, SmsStatusReportEventArgs.CreateFromResponse(e.Line1, e.Line2)); else if (AtErrorParsers.TryGetError(e.Line1, out Error error)) ErrorReceived?.Invoke(this, new ErrorEventArgs(error.ToString())); else @@ -49,6 +51,8 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) public event EventHandler ErrorReceived; public event EventHandler GenericEvent; + public event EventHandler SmsStatusReportReceived; + #region _V_25TER public event EventHandler IncomingCall; public event EventHandler MissedCall; diff --git a/src/HeboTech.ATLib/Modems/IModem.cs b/src/HeboTech.ATLib/Modems/IModem.cs index 49cb28a..64dc1af 100644 --- a/src/HeboTech.ATLib/Modems/IModem.cs +++ b/src/HeboTech.ATLib/Modems/IModem.cs @@ -46,6 +46,9 @@ public interface IModem : IDisposable /// event EventHandler UssdResponseReceived; + + event EventHandler SmsStatusReportReceived; + /// /// Indicates that an event with no specific event handler is received /// diff --git a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs index be1f389..3300e77 100644 --- a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs +++ b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs @@ -2,12 +2,18 @@ { internal enum MessageTypeIndicator : byte { - SMS_DELIVER_REPORT = 0x00, + // MS -> SC SMS_DELIVER = 0x00, + // SC -> MS + SMS_DELIVER_REPORT = 0x00, + // MS -> SC SMS_SUBMIT = 0x01, + // SC -> MS SMS_SUBMIT_REPORT = 0x01, - SMS_COMMAND = 0x10, - SMS_STATUS_REPORT = 0x10, - Reserved = 0x11 + // MS -> SC + SMS_COMMAND = 0x02, + // SC -> MS + SMS_STATUS_REPORT = 0x02, + Reserved = 0x03 } } diff --git a/src/HeboTech.ATLib/PDU/PhoneNumberDecoder.cs b/src/HeboTech.ATLib/PDU/PhoneNumberDecoder.cs new file mode 100644 index 0000000..36e3004 --- /dev/null +++ b/src/HeboTech.ATLib/PDU/PhoneNumberDecoder.cs @@ -0,0 +1,48 @@ +using HeboTech.ATLib.CodingSchemes; +using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.Extensions; +using System; +using System.Linq; + +namespace HeboTech.ATLib.PDU +{ + internal class PhoneNumberDecoder + { + public static PhoneNumberDTO DecodePhoneNumber(ReadOnlySpan data) + { + byte ext_ton_npi = data[0]; + TypeOfNumber ton = (TypeOfNumber)((ext_ton_npi & 0b0111_0000) >> 4); + + string number = string.Empty; + switch (ton) + { + case TypeOfNumber.Unknown: + break; + case TypeOfNumber.International: + number = "+"; + break; + case TypeOfNumber.National: + break; + case TypeOfNumber.NetworkSpecific: + break; + case TypeOfNumber.Subscriber: + break; + case TypeOfNumber.AlphaNumeric: + var unpacked = Gsm7.Unpack(data[1..].ToArray()); + var decoded = Gsm7.DecodeFromBytes(unpacked); + return new PhoneNumberDTO(decoded); + case TypeOfNumber.Abbreviated: + break; + case TypeOfNumber.ReservedForExtension: + break; + default: + throw new NotImplementedException($"TON {ton} is not supported"); + } + + number += string.Join("", data[1..].ToArray().Select(x => x.SwapNibbles().ToString("X2"))); + if (number[^1] == 'F') + number = number[..^1]; + return new PhoneNumberDTO(number); + } + } +} diff --git a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs index 56c102f..3a00f7e 100644 --- a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs @@ -1,9 +1,7 @@ using HeboTech.ATLib.CodingSchemes; using HeboTech.ATLib.DTOs; -using HeboTech.ATLib.Extensions; using System; using System.Linq; -using System.Threading; namespace HeboTech.ATLib.PDU { @@ -65,7 +63,7 @@ public static SmsDeliver Decode(ReadOnlySpan bytes, int timestampYearOffse PhoneNumberDTO serviceCenterNumber = null; if (smsc_length > 0) { - serviceCenterNumber = DecodePhoneNumber(bytes[offset..(offset += smsc_length)]); + serviceCenterNumber = PhoneNumberDecoder.DecodePhoneNumber(bytes[offset..(offset += smsc_length)]); } // SMS-DELIVER start @@ -78,7 +76,7 @@ public static SmsDeliver Decode(ReadOnlySpan bytes, int timestampYearOffse PhoneNumberDTO oa = null; if (tp_oa_bytes_length > 0) { - oa = DecodePhoneNumber(bytes[offset..(offset += tp_oa_bytes_length)]); + oa = PhoneNumberDecoder.DecodePhoneNumber(bytes[offset..(offset += tp_oa_bytes_length)]); } byte tp_pid = bytes[offset++]; @@ -143,42 +141,5 @@ public static SmsDeliver Decode(ReadOnlySpan bytes, int timestampYearOffse else return new SmsDeliver(serviceCenterNumber, oa, message, scts); } - - private static PhoneNumberDTO DecodePhoneNumber(ReadOnlySpan data) - { - byte ext_ton_npi = data[0]; - TypeOfNumber ton = (TypeOfNumber)((ext_ton_npi & 0b0111_0000) >> 4); - - string number = string.Empty; - switch (ton) - { - case TypeOfNumber.Unknown: - break; - case TypeOfNumber.International: - number = "+"; - break; - case TypeOfNumber.National: - break; - case TypeOfNumber.NetworkSpecific: - break; - case TypeOfNumber.Subscriber: - break; - case TypeOfNumber.AlphaNumeric: - var unpacked = Gsm7.Unpack(data[1..].ToArray()); - var decoded = Gsm7.DecodeFromBytes(unpacked); - return new PhoneNumberDTO(decoded); - case TypeOfNumber.Abbreviated: - break; - case TypeOfNumber.ReservedForExtension: - break; - default: - throw new NotImplementedException($"TON {ton} is not supported"); - } - - number += string.Join("", data[1..].ToArray().Select(x => x.SwapNibbles().ToString("X2"))); - if (number[^1] == 'F') - number = number[..^1]; - return new PhoneNumberDTO(number); - } } } diff --git a/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs b/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs new file mode 100644 index 0000000..c302b2e --- /dev/null +++ b/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs @@ -0,0 +1,74 @@ +using HeboTech.ATLib.DTOs; +using System; + +namespace HeboTech.ATLib.PDU +{ + public static class SmsStatusReportDecoder + { + private class SmsStatusReportHeader + { + private SmsStatusReportHeader() + { + } + + public SmsStatusReportHeader(MessageTypeIndicator mti, bool mms, bool lp, bool sri, bool udhi, bool rp) + { + MTI = mti; + MMS = mms; + LP = lp; + SRI = sri; + UDHI = udhi; + RP = rp; + } + + public MessageTypeIndicator MTI { get; private set; } + public bool MMS { get; private set; } + public bool LP { get; private set; } + public bool SRI { get; private set; } + public bool UDHI { get; private set; } + public bool RP { get; private set; } + + public static SmsStatusReportHeader Parse(byte header) + { + SmsStatusReportHeader parsedHeader = new SmsStatusReportHeader(); + + parsedHeader.MTI = (MessageTypeIndicator)(header & 0b0000_0011); + if (parsedHeader.MTI != MessageTypeIndicator.SMS_STATUS_REPORT) + throw new ArgumentException("Invalid SMS_STATUS_REPORT data"); + + parsedHeader.MMS = (header & 0b0000_0100) != 0; + parsedHeader.SRI = (header & 0b0000_1000) != 0; + parsedHeader.UDHI = (header & 0b0100_0000) != 0; + parsedHeader.RP = (header & 0b1000_0000) != 0; + + return parsedHeader; + } + } + + public static SmsStatusReport Decode(byte length, ReadOnlySpan bytes, int timestampYearOffset = 2000) + { + int offset = 0; + + // SMSC information + byte smsc_length = (byte)(bytes.Length - length); + PhoneNumberDTO serviceCenterNumber = null; + if (smsc_length > 0) + { + serviceCenterNumber = PhoneNumberDecoder.DecodePhoneNumber(bytes[offset..(offset += smsc_length)]); + } + + // SMS-STATUS-REPORT start + byte headerByte = bytes[offset++]; + SmsStatusReportHeader header = SmsStatusReportHeader.Parse(headerByte); + + byte tp_mr = bytes[offset++]; + byte tp_ra_length = (byte)((bytes[offset++] / 2) + 1); + ReadOnlySpan tp_ra = bytes[offset..(offset += tp_ra_length)]; + ReadOnlySpan tp_scts = bytes[offset..(offset += 7)]; + ReadOnlySpan tp_dt = bytes[offset..(offset += 7)]; + byte tp_st = bytes[offset++]; + + return new SmsStatusReport(tp_mr, PhoneNumberDecoder.DecodePhoneNumber(tp_ra), TpduTime.DecodeTimestamp(tp_scts, timestampYearOffset), TpduTime.DecodeTimestamp(tp_dt, timestampYearOffset), (SmsDeliveryStatus) tp_st); + } + } +} diff --git a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs index 1110d9d..0fe477e 100644 --- a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs @@ -59,6 +59,7 @@ public static IEnumerable Encode(SmsSubmitRequest smsSubmit) .Initialize() .DestinationAddress(smsSubmit.PhoneNumber) .ValidityPeriod(smsSubmit.ValidityPeriod) + .EnableStatusReportRequest() .Message(smsSubmit.Message, smsSubmit.CodingScheme, smsSubmit.MessageReferenceNumber) .Build(); From 85e732b2df71c594a871306b3859b5dbbfe3b380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Thu, 15 Feb 2024 16:14:23 +0100 Subject: [PATCH 2/6] Temp --- .../FunctionalityTest.cs | 59 +++++++++++++------ src/HeboTech.ATLib.TestConsole/StressTest.cs | 4 +- src/HeboTech.ATLib/DTOs/BroadcastMessage.cs | 9 +++ src/HeboTech.ATLib/DTOs/SmsDeliver.cs | 5 ++ src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs | 13 ++-- src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs | 1 + .../BreadcastMessageReceivedEventArgs.cs | 27 +++++++++ ...essageStorageReferenceReceivedEventArgs.cs | 28 +++++++++ .../Events/SmsReceivedEventArgs.cs | 26 ++++---- .../SmsReceivedStorageReferenceEventArgs.cs | 28 +++++++++ .../Events/SmsStatusReportEventArgs.cs | 2 +- ...msStatusReportStorageReferenceEventArgs.cs | 30 ++++++++++ src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs | 2 +- .../Modems/Generic/ModemBase.cs | 42 +++++++------ src/HeboTech.ATLib/Modems/IModem.cs | 9 ++- .../PDU/MessageTypeIndicator.cs | 31 +++++++--- src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs | 16 ++--- .../PDU/SmsStatusReportDecoder.cs | 30 +++++----- src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs | 20 +++---- 19 files changed, 285 insertions(+), 97 deletions(-) create mode 100644 src/HeboTech.ATLib/DTOs/BroadcastMessage.cs create mode 100644 src/HeboTech.ATLib/Events/BreadcastMessageReceivedEventArgs.cs create mode 100644 src/HeboTech.ATLib/Events/BreadcastMessageStorageReferenceReceivedEventArgs.cs create mode 100644 src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs create mode 100644 src/HeboTech.ATLib/Events/SmsStatusReportStorageReferenceEventArgs.cs diff --git a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs index d172e1b..d3adfb1 100644 --- a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs +++ b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs @@ -17,19 +17,29 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) { using AtChannel atChannel = AtChannel.Create(stream); //atChannel.EnableDebug((string line) => Console.WriteLine(line)); - using IDWM222 modem = new DWM222(atChannel); + using IMC55i modem = new MC55i(atChannel); atChannel.Open(); await atChannel.ClearAsync(); + modem.ErrorReceived += Modem_ErrorReceived; + + modem.GenericEvent += Modem_GenericEvent; + modem.IncomingCall += Modem_IncomingCall; modem.MissedCall += Modem_MissedCall; modem.CallStarted += Modem_CallStarted; modem.CallEnded += Modem_CallEnded; + + modem.SmsStorageReferenceReceived += Modem_SmsStorageReferenceReceived; modem.SmsReceived += Modem_SmsReceived; - modem.UssdResponseReceived += Modem_UssdResponseReceived; - modem.ErrorReceived += Modem_ErrorReceived; - modem.GenericEvent += Modem_GenericEvent; + modem.SmsStatusReportReceived += Modem_SmsStatusReportReceived; + modem.SmsStatusReportStorageReferenceReceived += Modem_SmsStatusReportStorageReferenceReceived; + + modem.BroadcastMessageReceived += Modem_BroadcastMessageReceived; + modem.BroadcastMessageStorageReferenceReceived += Modem_BroadcastMessageStorageReferenceReceived; + + modem.UssdResponseReceived += Modem_UssdResponseReceived; // Configure modem with required settings before PIN var requiredSettingsBeforePin = await modem.SetRequiredSettingsBeforePinAsync(); @@ -83,14 +93,6 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) var batteryStatus = await modem.GetBatteryStatusAsync(); Console.WriteLine($"Battery Status: {batteryStatus}"); - { - if (modem is IMC55i mc55i) - { - var mc55iBatteryStatus = await mc55i.MC55i_GetBatteryStatusAsync(); - Console.WriteLine($"MC55i Battery Status: {mc55iBatteryStatus}"); - } - } - var productInfo = await modem.GetProductIdentificationInformationAsync(); Console.WriteLine($"Product Information:{Environment.NewLine}{productInfo}"); @@ -100,8 +102,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) var dateTime = await modem.GetDateTimeAsync(); Console.WriteLine($"Date and time: {dateTime}"); - - var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 1, 0); + var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 2, 1, 1); // 2, 2, 2, 1, 1 -> PDU Console.WriteLine($"Setting new SMS indication: {newSmsIndicationResult}"); var supportedStorages = await modem.GetSupportedPreferredMessageStoragesAsync(); @@ -224,6 +225,31 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) } } + private static void Modem_BroadcastMessageStorageReferenceReceived(object sender, BreadcastMessageStorageReferenceReceivedEventArgs e) + { + Console.WriteLine($"Broadcast Message. Index {e.Index} at storage location {e.Storage}"); + } + + private static void Modem_SmsStorageReferenceReceived(object sender, SmsReceivedStorageReferenceEventArgs e) + { + Console.WriteLine($"SMS Deliver. Index {e.Index} at storage location {e.Storage}"); + } + + private static void Modem_SmsStatusReportStorageReferenceReceived(object sender, SmsStatusReportStorageReferenceEventArgs e) + { + Console.WriteLine($"SMS Status Report. Index {e.Index} at storage location {e.Storage}"); + } + + private static void Modem_BroadcastMessageReceived(object sender, BreadcastMessageReceivedEventArgs e) + { + Console.WriteLine($"Broadcast Message: {e.BroadcastMessage}"); + } + + private static void Modem_SmsReceived(object sender, SmsReceivedEventArgs e) + { + Console.WriteLine($"SMS Deliver: {e.SmsDeliver}"); + } + private static void Modem_SmsStatusReportReceived(object sender, SmsStatusReportEventArgs e) { Console.WriteLine($"SMS Status Report: {e.SmsStatusReport}"); @@ -255,11 +281,6 @@ private static void Modem_CallStarted(object sender, CallStartedEventArgs e) Console.WriteLine("Call started"); } - private static void Modem_SmsReceived(object sender, SmsReceivedEventArgs e) - { - Console.WriteLine($"SMS received. Index {e.Index} at storage location {e.Storage}"); - } - private static void Modem_MissedCall(object sender, MissedCallEventArgs e) { Console.WriteLine($"Missed call at {e.Time} from {e.PhoneNumber}"); diff --git a/src/HeboTech.ATLib.TestConsole/StressTest.cs b/src/HeboTech.ATLib.TestConsole/StressTest.cs index a877474..77f1e1f 100644 --- a/src/HeboTech.ATLib.TestConsole/StressTest.cs +++ b/src/HeboTech.ATLib.TestConsole/StressTest.cs @@ -20,7 +20,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) modem.IncomingCall += Modem_IncomingCall; modem.MissedCall += Modem_MissedCall; - modem.SmsReceived += Modem_SmsReceived; + modem.SmsStorageReferenceReceived += Modem_SmsReceived; await modem.DisableEchoAsync(); @@ -73,7 +73,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) Console.ReadKey(); } - private static void Modem_SmsReceived(object sender, Events.SmsReceivedEventArgs e) + private static void Modem_SmsReceived(object sender, Events.SmsReceivedStorageReferenceEventArgs e) { Console.WriteLine($"SMS received. Index {e.Index} at storage location {e.Storage}"); } diff --git a/src/HeboTech.ATLib/DTOs/BroadcastMessage.cs b/src/HeboTech.ATLib/DTOs/BroadcastMessage.cs new file mode 100644 index 0000000..02baf27 --- /dev/null +++ b/src/HeboTech.ATLib/DTOs/BroadcastMessage.cs @@ -0,0 +1,9 @@ +namespace HeboTech.ATLib.DTOs +{ + public class BroadcastMessage + { + public BroadcastMessage() + { + } + } +} diff --git a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs index 05e14dd..5645364 100644 --- a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs +++ b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs @@ -33,5 +33,10 @@ public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumbe public int MessageReferenceNumber { get; } public int TotalNumberOfParts { get; } public int PartNumber { get; } + + public override string ToString() + { + return $"From: {SenderNumber}. Message: {Message}. Timestamp: {Timestamp}"; + } } } diff --git a/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs b/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs index 4dc2a84..245610a 100644 --- a/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs +++ b/src/HeboTech.ATLib/DTOs/SmsDeliveryStatus.cs @@ -5,9 +5,12 @@ /// public enum SmsDeliveryStatus : byte { + // Suffix _1, _2 and _3 are used to separate identical names, one suffix for each 'group' of messages + + // Transaction completed Message_received_by_SME = 0b0000_0000, Forwarded_to_SME_but_unconfirmed_delivery = 0b0000_0001, - Message_replaced_by_SC = 0b0000_0010, + Message_replaced_by_the_SC = 0b0000_0010, // 000_0011..000_1111 Reserved // 001_0000..0011111 Values specific to each SC @@ -38,12 +41,12 @@ public enum SmsDeliveryStatus : byte // 1010000..1011111 Values specific to each SC // Temporary error, SC is not making any more transfer attempts - Congestion_2 = 0b0110_0000, - SME_busy_2 = 0b0110_0001, + Congestion_3 = 0b0110_0000, + SME_busy_3 = 0b0110_0001, No_response_from_SME = 0b0110_0010, - Service_rejected_2 = 0b0110_0011, + Service_rejected_3 = 0b0110_0011, Quality_of_service_not_available_3 = 0b0110_0100, - Error_in_SME_2 = 0b0110_0101, + Error_in_SME_3 = 0b0110_0101, //1100110..1101001 Reserved //1101010..1101111 Reserved diff --git a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs index a9e0590..55f24fe 100644 --- a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs +++ b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs @@ -67,5 +67,6 @@ public SmsSubmitRequest( public bool IncludeEmptySmscLength { get; set; } public byte MessageReferenceNumber { get; set; } public ValidityPeriod ValidityPeriod { get; set; } + public bool EnableStatusReportRequest { get; set; } } } diff --git a/src/HeboTech.ATLib/Events/BreadcastMessageReceivedEventArgs.cs b/src/HeboTech.ATLib/Events/BreadcastMessageReceivedEventArgs.cs new file mode 100644 index 0000000..cd7d50a --- /dev/null +++ b/src/HeboTech.ATLib/Events/BreadcastMessageReceivedEventArgs.cs @@ -0,0 +1,27 @@ +using HeboTech.ATLib.DTOs; +using System; +using System.Text.RegularExpressions; + +namespace HeboTech.ATLib.Events +{ + public class BreadcastMessageReceivedEventArgs + { + public BreadcastMessageReceivedEventArgs(BroadcastMessage broadcastMessage) + { + BroadcastMessage = broadcastMessage; + } + + public BroadcastMessage BroadcastMessage { get; } + + public static BreadcastMessageReceivedEventArgs CreateFromResponse(string line1, string line2) + { + var line1Match = Regex.Match(line1, @"\+CBM:\s(?\d+)"); + if (line1Match.Success) + { + throw new NotImplementedException(); + } + + return default; + } + } +} diff --git a/src/HeboTech.ATLib/Events/BreadcastMessageStorageReferenceReceivedEventArgs.cs b/src/HeboTech.ATLib/Events/BreadcastMessageStorageReferenceReceivedEventArgs.cs new file mode 100644 index 0000000..08bcc8b --- /dev/null +++ b/src/HeboTech.ATLib/Events/BreadcastMessageStorageReferenceReceivedEventArgs.cs @@ -0,0 +1,28 @@ +using System.Text.RegularExpressions; + +namespace HeboTech.ATLib.Events +{ + public class BreadcastMessageStorageReferenceReceivedEventArgs + { + public BreadcastMessageStorageReferenceReceivedEventArgs(string storage, int index) + { + Storage = storage; + Index = index; + } + + public string Storage { get; } + public int Index { get; } + + public static BreadcastMessageStorageReferenceReceivedEventArgs CreateFromResponse(string response) + { + var match = Regex.Match(response, @"\+CBMI:\s""(?[A-Z]+)"",(?\d+)"); + if (match.Success) + { + string storage = match.Groups["storage"].Value; + int index = int.Parse(match.Groups["index"].Value); + return new BreadcastMessageStorageReferenceReceivedEventArgs(storage, index); + } + return default; + } + } +} diff --git a/src/HeboTech.ATLib/Events/SmsReceivedEventArgs.cs b/src/HeboTech.ATLib/Events/SmsReceivedEventArgs.cs index cbd4c19..613cac9 100644 --- a/src/HeboTech.ATLib/Events/SmsReceivedEventArgs.cs +++ b/src/HeboTech.ATLib/Events/SmsReceivedEventArgs.cs @@ -1,27 +1,29 @@ -using System.Text.RegularExpressions; +using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.Extensions; +using HeboTech.ATLib.PDU; +using System.Text.RegularExpressions; namespace HeboTech.ATLib.Events { public class SmsReceivedEventArgs { - public SmsReceivedEventArgs(string storage, int index) + public SmsReceivedEventArgs(SmsDeliver smsDeliver) { - Storage = storage; - Index = index; + SmsDeliver = smsDeliver; } - public string Storage { get; } - public int Index { get; } + public SmsDeliver SmsDeliver { get; } - public static SmsReceivedEventArgs CreateFromResponse(string response) + public static SmsReceivedEventArgs CreateFromResponse(string line1, string line2) { - var match = Regex.Match(response, @"\+CMTI:\s""(?[A-Z]+)"",(?\d+)"); - if (match.Success) + var line1Match = Regex.Match(line1, @"\+CMT:\s(""(?[\+0-9]*)"")?,(?\d+)"); + if (line1Match.Success) { - string storage = match.Groups["storage"].Value; - int index = int.Parse(match.Groups["index"].Value); - return new SmsReceivedEventArgs(storage, index); + byte length = byte.Parse(line1Match.Groups["length"].Value); + var smsDeliver = SmsDeliverDecoder.Decode(line2.ToByteArray()); + return new SmsReceivedEventArgs(smsDeliver); } + return default; } } diff --git a/src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs b/src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs new file mode 100644 index 0000000..b91a75d --- /dev/null +++ b/src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs @@ -0,0 +1,28 @@ +using System.Text.RegularExpressions; + +namespace HeboTech.ATLib.Events +{ + public class SmsReceivedStorageReferenceEventArgs + { + public SmsReceivedStorageReferenceEventArgs(string storage, int index) + { + Storage = storage; + Index = index; + } + + public string Storage { get; } + public int Index { get; } + + public static SmsReceivedStorageReferenceEventArgs CreateFromResponse(string response) + { + var match = Regex.Match(response, @"\+CMTI:\s""(?[A-Z]+)"",(?\d+)"); + if (match.Success) + { + string storage = match.Groups["storage"].Value; + int index = int.Parse(match.Groups["index"].Value); + return new SmsReceivedStorageReferenceEventArgs(storage, index); + } + return default; + } + } +} diff --git a/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs b/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs index 80e25ec..ccdbd58 100644 --- a/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs +++ b/src/HeboTech.ATLib/Events/SmsStatusReportEventArgs.cs @@ -20,7 +20,7 @@ public static SmsStatusReportEventArgs CreateFromResponse(string line1, string l if (line1Match.Success) { byte length = byte.Parse(line1Match.Groups["length"].Value); - var report = SmsStatusReportDecoder.Decode(length, line2.ToByteArray()); + var report = SmsStatusReportDecoder.Decode(line2.ToByteArray(), length); return new SmsStatusReportEventArgs(report); } diff --git a/src/HeboTech.ATLib/Events/SmsStatusReportStorageReferenceEventArgs.cs b/src/HeboTech.ATLib/Events/SmsStatusReportStorageReferenceEventArgs.cs new file mode 100644 index 0000000..f3b0ec3 --- /dev/null +++ b/src/HeboTech.ATLib/Events/SmsStatusReportStorageReferenceEventArgs.cs @@ -0,0 +1,30 @@ +using HeboTech.ATLib.Modems.Generic; +using System.Text.RegularExpressions; + +namespace HeboTech.ATLib.Events +{ + public class SmsStatusReportStorageReferenceEventArgs + { + public SmsStatusReportStorageReferenceEventArgs(MessageStorage storage, int index) + { + Storage = storage; + Index = index; + } + + public MessageStorage Storage { get; } + public int Index { get; } + + public static SmsStatusReportStorageReferenceEventArgs CreateFromResponse(string line1) + { + var match = Regex.Match(line1, @"\+CDSI:\s""(?[a-zA-Z]+)"",(?\d+)"); + if (match.Success) + { + string storage = match.Groups["storage"].Value; + int index = int.Parse(match.Groups["index"].Value); + return new SmsStatusReportStorageReferenceEventArgs((MessageStorage)storage, index); + } + + return default; + } + } +} diff --git a/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs b/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs index b3afccd..dbb567c 100644 --- a/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs +++ b/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs @@ -33,7 +33,7 @@ public async Task>> SendSmsAsync(PhoneNu if (message is null) throw new ArgumentNullException(nameof(message)); - IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message) { IncludeEmptySmscLength = true }); + IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message) { IncludeEmptySmscLength = true, EnableStatusReportRequest = true }); List> references = new List>(); foreach (string pdu in pdus) { diff --git a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs index 45a1126..11fdec4 100644 --- a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs +++ b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs @@ -36,14 +36,28 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) CallEnded?.Invoke(this, CallEndedEventArgs.CreateFromResponse(e.Line1)); else if (e.Line1.StartsWith("MISSED_CALL: ")) MissedCall?.Invoke(this, MissedCallEventArgs.CreateFromResponse(e.Line1)); + + else if (e.Line1.StartsWith("+CMT: ")) + SmsReceived?.Invoke(this, SmsReceivedEventArgs.CreateFromResponse(e.Line1, e.Line2)); else if (e.Line1.StartsWith("+CMTI: ")) - SmsReceived?.Invoke(this, SmsReceivedEventArgs.CreateFromResponse(e.Line1)); - else if (e.Line1.StartsWith("+CUSD: ")) - UssdResponseReceived?.Invoke(this, UssdResponseEventArgs.CreateFromResponse(e.Line1)); + SmsStorageReferenceReceived?.Invoke(this, SmsReceivedStorageReferenceEventArgs.CreateFromResponse(e.Line1)); + + else if (e.Line1.StartsWith("+CBM: ")) + BroadcastMessageReceived?.Invoke(this, BreadcastMessageReceivedEventArgs.CreateFromResponse(e.Line1, e.Line2)); + else if (e.Line1.StartsWith("+CBMI: ")) + BroadcastMessageStorageReferenceReceived?.Invoke(this, BreadcastMessageStorageReferenceReceivedEventArgs.CreateFromResponse(e.Line1)); + else if (e.Line1.StartsWith("+CDS: ")) SmsStatusReportReceived?.Invoke(this, SmsStatusReportEventArgs.CreateFromResponse(e.Line1, e.Line2)); + else if (e.Line1.StartsWith("+CDSI: ")) + SmsStatusReportStorageReferenceReceived?.Invoke(this, SmsStatusReportStorageReferenceEventArgs.CreateFromResponse(e.Line1)); + + else if (e.Line1.StartsWith("+CUSD: ")) + UssdResponseReceived?.Invoke(this, UssdResponseEventArgs.CreateFromResponse(e.Line1)); + else if (AtErrorParsers.TryGetError(e.Line1, out Error error)) ErrorReceived?.Invoke(this, new ErrorEventArgs(error.ToString())); + else GenericEvent?.Invoke(this, new GenericEventArgs(e.Line1)); } @@ -51,7 +65,14 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) public event EventHandler ErrorReceived; public event EventHandler GenericEvent; + public event EventHandler SmsReceived; + public event EventHandler SmsStorageReferenceReceived; + + public event EventHandler BroadcastMessageReceived; + public event EventHandler BroadcastMessageStorageReferenceReceived; + public event EventHandler SmsStatusReportReceived; + public event EventHandler SmsStatusReportStorageReferenceReceived; #region _V_25TER public event EventHandler IncomingCall; @@ -190,8 +211,6 @@ public virtual async Task SetCharacterSetAsync(CharacterSet chara #endregion #region _3GPP_TS_27_005 - public event EventHandler SmsReceived; - public virtual async Task> GetSmsMessageFormatAsync() { AtResponse response = await channel.SendSingleLineCommandAsync($"AT+CMGF?", "+CMGF:"); @@ -224,17 +243,6 @@ public virtual async Task SetSmsMessageFormatAsync(SmsTextFormat public virtual async Task SetNewSmsIndicationAsync(int mode, int mt, int bm, int ds, int bfr) { - if (mode < 0 || mode > 2) - throw new ArgumentOutOfRangeException(nameof(mode)); - if (mt < 0 || mt > 3) - throw new ArgumentOutOfRangeException(nameof(mt)); - if (!(bm == 0 || bm == 2)) - throw new ArgumentOutOfRangeException(nameof(bm)); - if (ds < 0 || ds > 2) - throw new ArgumentOutOfRangeException(nameof(ds)); - if (bfr < 0 || bfr > 1) - throw new ArgumentOutOfRangeException(nameof(bfr)); - AtResponse response = await channel.SendCommand($"AT+CNMI={mode},{mt},{bm},{ds},{bfr}"); if (response.Success) @@ -257,7 +265,7 @@ protected virtual async Task>> SendSmsAs if (message is null) throw new ArgumentNullException(nameof(message)); - IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message, codingScheme) { IncludeEmptySmscLength = includeEmptySmscLength }); + IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message, codingScheme) { IncludeEmptySmscLength = includeEmptySmscLength, EnableStatusReportRequest = true }); List> references = new List>(); foreach (string pdu in pdus) { diff --git a/src/HeboTech.ATLib/Modems/IModem.cs b/src/HeboTech.ATLib/Modems/IModem.cs index 64dc1af..c5ceed5 100644 --- a/src/HeboTech.ATLib/Modems/IModem.cs +++ b/src/HeboTech.ATLib/Modems/IModem.cs @@ -39,7 +39,7 @@ public interface IModem : IDisposable /// /// Indicates that an SMS is received /// - event EventHandler SmsReceived; + event EventHandler SmsStorageReferenceReceived; /// /// Indicates that a USSD response is received @@ -47,7 +47,14 @@ public interface IModem : IDisposable event EventHandler UssdResponseReceived; + event EventHandler SmsReceived; + + event EventHandler BroadcastMessageReceived; + event EventHandler BroadcastMessageStorageReferenceReceived; + event EventHandler SmsStatusReportReceived; + event EventHandler SmsStatusReportStorageReferenceReceived; + /// /// Indicates that an event with no specific event handler is received diff --git a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs index 3300e77..c5f625b 100644 --- a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs +++ b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs @@ -1,19 +1,36 @@ namespace HeboTech.ATLib.PDU { - internal enum MessageTypeIndicator : byte + /// + /// Inbound (SMSC/SC -> MS) + /// SMSC: Short Message Service Center + /// SC: SMS Center + /// MS: Mobile Station + /// + internal enum MessageTypeIndicatorInbound : byte { - // MS -> SC - SMS_DELIVER = 0x00, // SC -> MS SMS_DELIVER_REPORT = 0x00, - // MS -> SC - SMS_SUBMIT = 0x01, // SC -> MS SMS_SUBMIT_REPORT = 0x01, - // MS -> SC - SMS_COMMAND = 0x02, // SC -> MS SMS_STATUS_REPORT = 0x02, Reserved = 0x03 } + + /// + /// Outbound (MS -> SMSC/SC) + /// SMSC: Short Message Service Center + /// SC: SMS Center + /// MS: Mobile Station + /// + internal enum MessageTypeIndicatorOutbound : byte + { + // MS -> SC + SMS_DELIVER = 0x00, + // MS -> SC + SMS_SUBMIT = 0x01, + // MS -> SC + SMS_COMMAND = 0x02, + Reserved = 0x03 + } } diff --git a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs index 3a00f7e..d858739 100644 --- a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs @@ -13,7 +13,7 @@ private SmsDeliverHeader() { } - public SmsDeliverHeader(MessageTypeIndicator mti, bool mms, bool lp, bool sri, bool udhi, bool rp) + public SmsDeliverHeader(MessageTypeIndicatorOutbound mti, bool mms, bool lp, bool sri, bool udhi, bool rp) { MTI = mti; MMS = mms; @@ -23,7 +23,7 @@ public SmsDeliverHeader(MessageTypeIndicator mti, bool mms, bool lp, bool sri, b RP = rp; } - public MessageTypeIndicator MTI { get; private set; } + public MessageTypeIndicatorOutbound MTI { get; private set; } public bool MMS { get; private set; } public bool LP { get; private set; } public bool SRI { get; private set; } @@ -34,14 +34,14 @@ public static SmsDeliverHeader Parse(byte header) { SmsDeliverHeader parsedHeader = new SmsDeliverHeader(); - parsedHeader.MTI = (MessageTypeIndicator)(header & 0b0000_0011); - if (parsedHeader.MTI != (byte)MessageTypeIndicator.SMS_DELIVER) + parsedHeader.MTI = (MessageTypeIndicatorOutbound)(header & 0b0000_0011); + if (parsedHeader.MTI != (byte)MessageTypeIndicatorOutbound.SMS_DELIVER) throw new ArgumentException("Invalid SMS-DELIVER data"); - parsedHeader.MMS = (header & 0b0000_0100) != 0; - parsedHeader.SRI = (header & 0b0000_1000) != 0; - parsedHeader.UDHI = (header & 0b0100_0000) != 0; - parsedHeader.RP = (header & 0b1000_0000) != 0; + parsedHeader.MMS = (header & (1 << 2)) != 0; + parsedHeader.SRI = (header & (1 << 3)) != 0; + parsedHeader.UDHI = (header & (1 << 6)) != 0; + parsedHeader.RP = (header & (1 << 7)) != 0; return parsedHeader; } diff --git a/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs b/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs index c302b2e..496628e 100644 --- a/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsStatusReportDecoder.cs @@ -11,46 +11,48 @@ private SmsStatusReportHeader() { } - public SmsStatusReportHeader(MessageTypeIndicator mti, bool mms, bool lp, bool sri, bool udhi, bool rp) + public SmsStatusReportHeader(MessageTypeIndicatorInbound mti, bool mms, bool lp, bool sri, bool udhi) { MTI = mti; MMS = mms; LP = lp; - SRI = sri; + SRQ = sri; UDHI = udhi; - RP = rp; } - public MessageTypeIndicator MTI { get; private set; } + public MessageTypeIndicatorInbound MTI { get; private set; } public bool MMS { get; private set; } public bool LP { get; private set; } - public bool SRI { get; private set; } + /// + /// If set - this is a result of an SMS-COMMAND; otherwise it is a result of an SMS-SUBMIT + /// 9 2 2 3 + /// + public bool SRQ { get; private set; } public bool UDHI { get; private set; } - public bool RP { get; private set; } public static SmsStatusReportHeader Parse(byte header) { SmsStatusReportHeader parsedHeader = new SmsStatusReportHeader(); - parsedHeader.MTI = (MessageTypeIndicator)(header & 0b0000_0011); - if (parsedHeader.MTI != MessageTypeIndicator.SMS_STATUS_REPORT) + parsedHeader.MTI = (MessageTypeIndicatorInbound)(header & (3 << 0)); + if (parsedHeader.MTI != MessageTypeIndicatorInbound.SMS_STATUS_REPORT) throw new ArgumentException("Invalid SMS_STATUS_REPORT data"); - parsedHeader.MMS = (header & 0b0000_0100) != 0; - parsedHeader.SRI = (header & 0b0000_1000) != 0; - parsedHeader.UDHI = (header & 0b0100_0000) != 0; - parsedHeader.RP = (header & 0b1000_0000) != 0; + parsedHeader.MMS = (header & (1 << 2)) != 0; + parsedHeader.LP = (header & (1 << 3)) != 0; + parsedHeader.SRQ = (header & (1 << 5)) != 0; + parsedHeader.UDHI = (header & (1 << 6)) != 0; return parsedHeader; } } - public static SmsStatusReport Decode(byte length, ReadOnlySpan bytes, int timestampYearOffset = 2000) + public static SmsStatusReport Decode(ReadOnlySpan bytes, int timestampYearOffset = 2000) { int offset = 0; // SMSC information - byte smsc_length = (byte)(bytes.Length - length); + byte smsc_length = bytes[offset++]; PhoneNumberDTO serviceCenterNumber = null; if (smsc_length > 0) { diff --git a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs index 0fe477e..75c4bad 100644 --- a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs @@ -39,7 +39,7 @@ internal class SmsSubmitEncoder protected SmsSubmitEncoder() { - header = (byte)MessageTypeIndicator.SMS_SUBMIT; + header = (byte)MessageTypeIndicatorOutbound.SMS_SUBMIT; } protected static SmsSubmitEncoder Initialize() @@ -59,7 +59,7 @@ public static IEnumerable Encode(SmsSubmitRequest smsSubmit) .Initialize() .DestinationAddress(smsSubmit.PhoneNumber) .ValidityPeriod(smsSubmit.ValidityPeriod) - .EnableStatusReportRequest() + .EnableStatusReportRequest(smsSubmit.EnableStatusReportRequest) .Message(smsSubmit.Message, smsSubmit.CodingScheme, smsSubmit.MessageReferenceNumber) .Build(); @@ -81,7 +81,7 @@ public static IEnumerable Encode(SmsSubmitRequest smsSubmit) protected SmsSubmitEncoder EnableUserDataHeaderIndicator() { - header |= 0b0100_0000; + header |= (1 << 6); return this; } @@ -91,13 +91,13 @@ protected SmsSubmitEncoder EnableUserDataHeaderIndicator() /// protected SmsSubmitEncoder EnableReplyPath() { - header |= 0b1000_0000; + header |= (1 << 7); return this; } protected static byte GetAddressType(PhoneNumber phoneNumber) { - return (byte)(0b1000_0000 + ((byte)phoneNumber.GetTypeOfNumber() << 4) + (byte)phoneNumber.GetNumberPlanIdentification()); + return (byte)((1 << 7) + ((byte)phoneNumber.GetTypeOfNumber() << 4) + (byte)phoneNumber.GetNumberPlanIdentification()); } protected static string SwapPhoneNumberDigits(string data) @@ -121,7 +121,7 @@ protected static string SwapPhoneNumberDigits(string data) /// protected SmsSubmitEncoder RejectDuplicates() { - header |= 0b0000_0100; + header |= (1 << 2); return this; } @@ -133,8 +133,7 @@ protected SmsSubmitEncoder RejectDuplicates() protected SmsSubmitEncoder ValidityPeriod(ValidityPeriod validityPeriod) { // Set format - byte mask = 0b0001_1000; - header = (byte)((header & ~mask) | ((byte)validityPeriod.Format & mask)); + header |= (byte)((byte)validityPeriod.Format << 3); // Set value vp.Clear(); @@ -143,9 +142,10 @@ protected SmsSubmitEncoder ValidityPeriod(ValidityPeriod validityPeriod) return this; } - protected SmsSubmitEncoder EnableStatusReportRequest() + protected SmsSubmitEncoder EnableStatusReportRequest(bool enable) { - header |= 0b0010_0000; + if (enable) + header |= (1 << 5); return this; } From 0234a406a5ecb21f2f89c24a65d887e81821c185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Wed, 1 May 2024 22:32:59 +0200 Subject: [PATCH 3/6] Added support to add all message types --- .../FunctionalityTest.cs | 25 +- src/HeboTech.ATLib.TestConsole/StressTest.cs | 2 +- .../PDU/SmsSubmitEncoderTests.cs | 6 +- src/HeboTech.ATLib/DTOs/SmsBase.cs | 26 ++ src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs | 14 + src/HeboTech.ATLib/DTOs/SmsDeliver.cs | 17 +- src/HeboTech.ATLib/DTOs/SmsStatusReport.cs | 10 +- src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs | 1 - src/HeboTech.ATLib/DTOs/ValidityPeriod.cs | 262 ++++++++++++++++++ ...> SmsStorageReferenceReceivedEventArgs.cs} | 8 +- .../Extensions/SmsDeliverExtensions.cs | 17 -- .../Extensions/SmsExtensions.cs | 12 + src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs | 67 ++--- .../Modems/Generic/ModemBase.cs | 57 ++-- src/HeboTech.ATLib/Modems/IModem.cs | 27 +- src/HeboTech.ATLib/Modems/Qualcomm/MDM9225.cs | 12 +- src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs | 22 +- src/HeboTech.ATLib/Modems/TP-LINK/MA260.cs | 12 +- src/HeboTech.ATLib/Modems/Telit/ME910C1.cs | 9 +- .../PDU/MessageTypeIndicator.cs | 8 +- .../PDU/ReceivedMessageTypeParser.cs | 18 ++ src/HeboTech.ATLib/PDU/SmsDecoder.cs | 41 +++ src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs | 8 +- src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs | 15 +- 24 files changed, 515 insertions(+), 181 deletions(-) create mode 100644 src/HeboTech.ATLib/DTOs/SmsBase.cs create mode 100644 src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs rename src/HeboTech.ATLib/Events/{SmsReceivedStorageReferenceEventArgs.cs => SmsStorageReferenceReceivedEventArgs.cs} (72%) delete mode 100644 src/HeboTech.ATLib/Extensions/SmsDeliverExtensions.cs create mode 100644 src/HeboTech.ATLib/Extensions/SmsExtensions.cs create mode 100644 src/HeboTech.ATLib/PDU/ReceivedMessageTypeParser.cs create mode 100644 src/HeboTech.ATLib/PDU/SmsDecoder.cs diff --git a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs index d3adfb1..0255aa5 100644 --- a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs +++ b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs @@ -18,11 +18,12 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) using AtChannel atChannel = AtChannel.Create(stream); //atChannel.EnableDebug((string line) => Console.WriteLine(line)); using IMC55i modem = new MC55i(atChannel); + //using IDWM222 modem = new DWM222(atChannel); atChannel.Open(); await atChannel.ClearAsync(); modem.ErrorReceived += Modem_ErrorReceived; - + modem.GenericEvent += Modem_GenericEvent; modem.IncomingCall += Modem_IncomingCall; @@ -102,14 +103,17 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) var dateTime = await modem.GetDateTimeAsync(); Console.WriteLine($"Date and time: {dateTime}"); - var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 2, 1, 1); // 2, 2, 2, 1, 1 -> PDU + var selectMessageService = await modem.SetSelectMessageService(0); + Console.WriteLine($"Setting select message service: {selectMessageService}"); + + var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 2, 0); // 2, 2, 2, 1, 1 -> PDU (2, 1, 2, 1, 0) Console.WriteLine($"Setting new SMS indication: {newSmsIndicationResult}"); var supportedStorages = await modem.GetSupportedPreferredMessageStoragesAsync(); Console.WriteLine($"Supported storages:{Environment.NewLine}{supportedStorages}"); var currentStorages = await modem.GetPreferredMessageStoragesAsync(); Console.WriteLine($"Current storages:{Environment.NewLine}{currentStorages}"); - var setPreferredStorages = await modem.SetPreferredMessageStorageAsync(MessageStorage.SM, MessageStorage.SM, MessageStorage.SM); + var setPreferredStorages = await modem.SetPreferredMessageStorageAsync(MessageStorage.MT, MessageStorage.MT, MessageStorage.MT); Console.WriteLine($"Storages set:{Environment.NewLine}{setPreferredStorages}"); Console.WriteLine("Done. Press 'a' to answer call, 'd' to dial, 'h' to hang up, 's' to send SMS, 'r' to read an SMS, 'l' to list all SMSs, 'p' to delete an SMS, 'u' to send USSD code, 'x' to send raw command, 'z' to send raw command with response, '+' to enable debug, '-' to disable debug and 'q' to exit..."); @@ -167,7 +171,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) string smsMessage = Console.ReadLine(); Console.WriteLine("Sending SMS..."); - IEnumerable> smsReferences = await modem.SendSmsAsync(phoneNumber, smsMessage); + IEnumerable> smsReferences = await modem.SendSmsAsync(new SmsSubmitRequest(phoneNumber, smsMessage) { EnableStatusReportRequest = true, ValidityPeriod = ValidityPeriod.Relative(RelativeValidityPeriods.Minutes_5) }); foreach (var smsReference in smsReferences) Console.WriteLine($"SMS Reference: {smsReference}"); break; @@ -178,7 +182,10 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) if (int.TryParse(Console.ReadLine(), out int smsIndex)) { var sms = await modem.ReadSmsAsync(smsIndex); - Console.WriteLine(sms); + if (sms.Success) + { + Console.WriteLine(sms.Result); + } } else Console.WriteLine("Invalid SMS index"); @@ -205,10 +212,14 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) case ConsoleKey.L: Console.WriteLine("List all SMSs:"); var smss = await modem.ListSmssAsync(SmsStatus.ALL); + Console.WriteLine($"{smss.Result.Count} SMSs:"); if (smss.Success && smss.Result.Any()) { foreach (var sms in smss.Result) - Console.WriteLine($"------------------------------------------------{Environment.NewLine}{sms}"); + { + Console.WriteLine($"------------------------------------------------"); + Console.WriteLine($"Index: {sms.Index}, {sms.Sms}"); + } Console.WriteLine($"------------------------------------------------"); } @@ -230,7 +241,7 @@ private static void Modem_BroadcastMessageStorageReferenceReceived(object sender Console.WriteLine($"Broadcast Message. Index {e.Index} at storage location {e.Storage}"); } - private static void Modem_SmsStorageReferenceReceived(object sender, SmsReceivedStorageReferenceEventArgs e) + private static void Modem_SmsStorageReferenceReceived(object sender, SmsStorageReferenceReceivedEventArgs e) { Console.WriteLine($"SMS Deliver. Index {e.Index} at storage location {e.Storage}"); } diff --git a/src/HeboTech.ATLib.TestConsole/StressTest.cs b/src/HeboTech.ATLib.TestConsole/StressTest.cs index 77f1e1f..e32d3be 100644 --- a/src/HeboTech.ATLib.TestConsole/StressTest.cs +++ b/src/HeboTech.ATLib.TestConsole/StressTest.cs @@ -73,7 +73,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) Console.ReadKey(); } - private static void Modem_SmsReceived(object sender, Events.SmsReceivedStorageReferenceEventArgs e) + private static void Modem_SmsReceived(object sender, Events.SmsStorageReferenceReceivedEventArgs e) { Console.WriteLine($"SMS received. Index {e.Index} at storage location {e.Storage}"); } diff --git a/src/HeboTech.ATLib.Tests/PDU/SmsSubmitEncoderTests.cs b/src/HeboTech.ATLib.Tests/PDU/SmsSubmitEncoderTests.cs index 533fd1b..ccec72c 100644 --- a/src/HeboTech.ATLib.Tests/PDU/SmsSubmitEncoderTests.cs +++ b/src/HeboTech.ATLib.Tests/PDU/SmsSubmitEncoderTests.cs @@ -40,9 +40,8 @@ public void Encode_SmsSubmit_test(string countryCode, string subscriberNumber, s encodedMessage, dataCodingScheme) { - IncludeEmptySmscLength = includeEmptySmscLength, MessageReferenceNumber = 12 - }); + }, includeEmptySmscLength); Assert.Equal(answer, encoded.ToArray()); } @@ -57,10 +56,9 @@ public void Encode_SmsSubmit_message_too_long_test(string countryCode, string su new string('a', characterCount), dataCodingScheme) { - IncludeEmptySmscLength = includeEmptySmscLength, MessageReferenceNumber = 12 }; - Assert.Throws(() => SmsSubmitEncoder.Encode(request).ToList()); + Assert.Throws(() => SmsSubmitEncoder.Encode(request, includeEmptySmscLength).ToList()); } } } diff --git a/src/HeboTech.ATLib/DTOs/SmsBase.cs b/src/HeboTech.ATLib/DTOs/SmsBase.cs new file mode 100644 index 0000000..3bd602b --- /dev/null +++ b/src/HeboTech.ATLib/DTOs/SmsBase.cs @@ -0,0 +1,26 @@ +using HeboTech.ATLib.PDU; + +namespace HeboTech.ATLib.DTOs +{ + public class SmsBase + { + protected SmsBase(MessageTypeIndicatorInbound messageTypeIndicator) + { + MessageTypeIndicator = messageTypeIndicator; + } + + protected SmsBase(MessageTypeIndicatorInbound messageTypeIndicator, int messageReference) + : this(messageTypeIndicator) + { + MessageReference = messageReference; + } + + public int MessageReference { get; } + public MessageTypeIndicatorInbound MessageTypeIndicator { get; } + + public override string ToString() + { + return $"MTI: {MessageTypeIndicator}, Msg. ref.: {MessageReference}"; + } + } +} \ No newline at end of file diff --git a/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs b/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs new file mode 100644 index 0000000..e572d4a --- /dev/null +++ b/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs @@ -0,0 +1,14 @@ +namespace HeboTech.ATLib.DTOs +{ + public class SmsBaseWithIndex + { + public SmsBaseWithIndex(SmsBase sms, int index) + { + Sms = sms; + Index = index; + } + + public SmsBase Sms { get; } + public int Index { get; } + } +} \ No newline at end of file diff --git a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs index 5645364..07f5a7d 100644 --- a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs +++ b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs @@ -1,13 +1,15 @@ -using System; +using HeboTech.ATLib.PDU; +using System; namespace HeboTech.ATLib.DTOs { /// /// Data object for a received SMS /// - public class SmsDeliver + public class SmsDeliver : SmsBase { public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumber, string message, DateTimeOffset timestamp) + : base(MessageTypeIndicatorInbound.SMS_DELIVER) { ServiceCenterNumber = serviceCenterNumber; SenderNumber = senderNumber; @@ -15,13 +17,13 @@ public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumbe Timestamp = timestamp; } - public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumber, string message, DateTimeOffset timestamp, int messageReferenceNumber, int totalNumberOfParts, int partNumber) + public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumber, string message, DateTimeOffset timestamp, int messageReference, int totalNumberOfParts, int partNumber) + : base(MessageTypeIndicatorInbound.SMS_DELIVER, messageReference) { ServiceCenterNumber = serviceCenterNumber; SenderNumber = senderNumber; Message = message; Timestamp = timestamp; - MessageReferenceNumber = messageReferenceNumber; TotalNumberOfParts = totalNumberOfParts; PartNumber = partNumber; } @@ -30,13 +32,16 @@ public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumbe public PhoneNumberDTO SenderNumber { get; } public string Message { get; } public DateTimeOffset Timestamp { get; } - public int MessageReferenceNumber { get; } public int TotalNumberOfParts { get; } public int PartNumber { get; } + public void DeliverMethod() + { + } + public override string ToString() { - return $"From: {SenderNumber}. Message: {Message}. Timestamp: {Timestamp}"; + return base.ToString() + $" From: {SenderNumber}. Message: {Message}. Timestamp: {Timestamp}"; } } } diff --git a/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs index 01dc2ff..a250581 100644 --- a/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs +++ b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs @@ -1,19 +1,19 @@ -using System; +using HeboTech.ATLib.PDU; +using System; namespace HeboTech.ATLib.DTOs { - public class SmsStatusReport + public class SmsStatusReport : SmsBase { public SmsStatusReport(int messageReference, PhoneNumberDTO recipientAddress, DateTimeOffset serviceCenterTimestamp, DateTimeOffset dischargeTime, SmsDeliveryStatus status) + : base(MessageTypeIndicatorInbound.SMS_STATUS_REPORT, messageReference) { - MessageReference = messageReference; RecipientAddress = recipientAddress; ServiceCenterTimestamp = serviceCenterTimestamp; DischargeTime = dischargeTime; Status = status; } - public int MessageReference { get; } public PhoneNumberDTO RecipientAddress { get; } public DateTimeOffset ServiceCenterTimestamp { get; } public DateTimeOffset DischargeTime { get; } @@ -21,7 +21,7 @@ public SmsStatusReport(int messageReference, PhoneNumberDTO recipientAddress, Da public override string ToString() { - return $"SMS no. {MessageReference} delivered with status {Status}. RA: {RecipientAddress}. SCTS: {ServiceCenterTimestamp}. DT: {DischargeTime}."; + return base.ToString() + $" Delivered with status {Status}. RA: {RecipientAddress}. SCTS: {ServiceCenterTimestamp}. DT: {DischargeTime}."; } } } diff --git a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs index 55f24fe..1dc11d2 100644 --- a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs +++ b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs @@ -64,7 +64,6 @@ public SmsSubmitRequest( public PhoneNumber PhoneNumber { get; } public string Message { get; } public CharacterSet CodingScheme { get; } - public bool IncludeEmptySmscLength { get; set; } public byte MessageReferenceNumber { get; set; } public ValidityPeriod ValidityPeriod { get; set; } public bool EnableStatusReportRequest { get; set; } diff --git a/src/HeboTech.ATLib/DTOs/ValidityPeriod.cs b/src/HeboTech.ATLib/DTOs/ValidityPeriod.cs index 3c64718..d20b9fc 100644 --- a/src/HeboTech.ATLib/DTOs/ValidityPeriod.cs +++ b/src/HeboTech.ATLib/DTOs/ValidityPeriod.cs @@ -40,6 +40,8 @@ private ValidityPeriod(ValidityPeriodFormat format, byte[] value) /// public static ValidityPeriod Relative(byte value) => new ValidityPeriod(ValidityPeriodFormat.Relative, new byte[] { value }); + public static ValidityPeriod Relative(RelativeValidityPeriods value) => Relative((byte)value); + /// /// An absolute validity period /// @@ -51,4 +53,264 @@ public static ValidityPeriod Absolute(DateTimeOffset value) return new ValidityPeriod(ValidityPeriodFormat.Absolute, encoded); } } + + public enum RelativeValidityPeriods + { + Minutes_5 = 0, + Minutes_10 = 1, + Minutes_15 = 2, + Minutes_20 = 3, + Minutes_25 = 4, + Minutes_30 = 5, + Minutes_35 = 6, + Minutes_40 = 7, + Minutes_45 = 8, + Minutes_50 = 9, + Minutes_55 = 10, + Hours_1 = 11, + Hours_1_Minutes_5 = 12, + Hours_1_Minutes_10 = 13, + Hours_1_Minutes_15 = 14, + Hours_1_Minutes_20 = 15, + Hours_1_Minutes_25 = 16, + Hours_1_Minutes_30 = 17, + Hours_1_Minutes_35 = 18, + Hours_1_Minutes_40 = 19, + Hours_1_Minutes_45 = 20, + Hours_1_Minutes_50 = 21, + Hours_1_Minutes_55 = 22, + Hours_2 = 23, + Hours_2_Minutes_5 = 24, + Hours_2_Minutes_10 = 25, + Hours_2_Minutes_15 = 26, + Hours_2_Minutes_20 = 27, + Hours_2_Minutes_25 = 28, + Hours_2_Minutes_30 = 29, + Hours_2_Minutes_35 = 30, + Hours_2_Minutes_40 = 31, + Hours_2_Minutes_45 = 32, + Hours_2_Minutes_50 = 33, + Hours_2_Minutes_55 = 34, + Hours_3 = 35, + Hours_3_Minutes_5 = 36, + Hours_3_Minutes_10 = 37, + Hours_3_Minutes_15 = 38, + Hours_3_Minutes_20 = 39, + Hours_3_Minutes_25 = 40, + Hours_3_Minutes_30 = 41, + Hours_3_Minutes_35 = 42, + Hours_3_Minutes_40 = 43, + Hours_3_Minutes_45 = 44, + Hours_3_Minutes_50 = 45, + Hours_3_Minutes_55 = 46, + Hours_4 = 47, + Hours_4_Minutes_5 = 48, + Hours_4_Minutes_10 = 49, + Hours_4_Minutes_15 = 50, + Hours_4_Minutes_20 = 51, + Hours_4_Minutes_25 = 52, + Hours_4_Minutes_30 = 53, + Hours_4_Minutes_35 = 54, + Hours_4_Minutes_40 = 55, + Hours_4_Minutes_45 = 56, + Hours_4_Minutes_50 = 57, + Hours_4_Minutes_55 = 58, + Hours_5 = 59, + Hours_5_Minutes_5 = 60, + Hours_5_Minutes_10 = 61, + Hours_5_Minutes_15 = 62, + Hours_5_Minutes_20 = 63, + Hours_5_Minutes_25 = 64, + Hours_5_Minutes_30 = 65, + Hours_5_Minutes_35 = 66, + Hours_5_Minutes_40 = 67, + Hours_5_Minutes_45 = 68, + Hours_5_Minutes_50 = 69, + Hours_5_Minutes_55 = 70, + Hours_6 = 71, + Hours_6_Minutes_5 = 72, + Hours_6_Minutes_10 = 73, + Hours_6_Minutes_15 = 74, + Hours_6_Minutes_20 = 75, + Hours_6_Minutes_25 = 76, + Hours_6_Minutes_30 = 77, + Hours_6_Minutes_35 = 78, + Hours_6_Minutes_40 = 79, + Hours_6_Minutes_45 = 80, + Hours_6_Minutes_50 = 81, + Hours_6_Minutes_55 = 82, + Hours_7 = 83, + Hours_7_Minutes_5 = 84, + Hours_7_Minutes_10 = 85, + Hours_7_Minutes_15 = 86, + Hours_7_Minutes_20 = 87, + Hours_7_Minutes_25 = 88, + Hours_7_Minutes_30 = 89, + Hours_7_Minutes_35 = 90, + Hours_7_Minutes_40 = 91, + Hours_7_Minutes_45 = 92, + Hours_7_Minutes_50 = 93, + Hours_7_Minutes_55 = 94, + Hours_8 = 95, + Hours_8_Minutes_5 = 96, + Hours_8_Minutes_10 = 97, + Hours_8_Minutes_15 = 98, + Hours_8_Minutes_20 = 99, + Hours_8_Minutes_25 = 100, + Hours_8_Minutes_30 = 101, + Hours_8_Minutes_35 = 102, + Hours_8_Minutes_40 = 103, + Hours_8_Minutes_45 = 104, + Hours_8_Minutes_50 = 105, + Hours_8_Minutes_55 = 106, + Hours_9 = 107, + Hours_9_Minutes_5 = 108, + Hours_9_Minutes_10 = 109, + Hours_9_Minutes_15 = 110, + Hours_9_Minutes_20 = 111, + Hours_9_Minutes_25 = 112, + Hours_9_Minutes_30 = 113, + Hours_9_Minutes_35 = 114, + Hours_9_Minutes_40 = 115, + Hours_9_Minutes_45 = 116, + Hours_9_Minutes_50 = 117, + Hours_9_Minutes_55 = 118, + Hours_10 = 119, + Hours_10_Minutes_5 = 120, + Hours_10_Minutes_10 = 121, + Hours_10_Minutes_15 = 122, + Hours_10_Minutes_20 = 123, + Hours_10_Minutes_25 = 124, + Hours_10_Minutes_30 = 125, + Hours_10_Minutes_35 = 126, + Hours_10_Minutes_40 = 127, + Hours_10_Minutes_45 = 128, + Hours_10_Minutes_50 = 129, + Hours_10_Minutes_55 = 130, + Hours_11 = 131, + Hours_11_Minutes_5 = 132, + Hours_11_Minutes_10 = 133, + Hours_11_Minutes_15 = 134, + Hours_11_Minutes_20 = 135, + Hours_11_Minutes_25 = 136, + Hours_11_Minutes_30 = 137, + Hours_11_Minutes_35 = 138, + Hours_11_Minutes_40 = 139, + Hours_11_Minutes_45 = 140, + Hours_11_Minutes_50 = 141, + Hours_11_Minutes_55 = 142, + Hours_12 = 143, + Hours_12_Minutes_30 = 144, + Hours_13 = 145, + Hours_13_Minutes_30 = 146, + Hours_14 = 147, + Hours_14_Minutes_30 = 148, + Hours_15 = 149, + Hours_15_Minutes_30 = 150, + Hours_16 = 151, + Hours_16_Minutes_30 = 152, + Hours_17 = 153, + Hours_17_Minutes_30 = 154, + Hours_18 = 155, + Hours_18_Minutes_30 = 156, + Hours_19 = 157, + Hours_19_Minutes_30 = 158, + Hours_20 = 159, + Hours_20_Minutes_30 = 160, + Hours_21 = 161, + Hours_21_Minutes_30 = 162, + Hours_22 = 163, + Hours_22_Minutes_30 = 164, + Hours_23 = 165, + Hours_23_Minutes_30 = 166, + Hours_24 = 167, + Days_2 = 168, + Days_3 = 169, + Days_4 = 170, + Days_5 = 171, + Days_6 = 172, + Days_7 = 173, + Days_8 = 174, + Days_9 = 175, + Days_10 = 176, + Days_11 = 177, + Days_12 = 178, + Days_13 = 179, + Days_14 = 180, + Days_15 = 181, + Days_16 = 182, + Days_17 = 183, + Days_18 = 184, + Days_19 = 185, + Days_20 = 186, + Days_21 = 187, + Days_22 = 188, + Days_23 = 189, + Days_24 = 190, + Days_25 = 191, + Days_26 = 192, + Days_27 = 193, + Days_28 = 194, + Days_29 = 195, + Days_30 = 196, + Weeks_5 = 197, + Weeks_6 = 198, + Weeks_7 = 199, + Weeks_8 = 200, + Weeks_9 = 201, + Weeks_10 = 202, + Weeks_11 = 203, + Weeks_12 = 204, + Weeks_13 = 205, + Weeks_14 = 206, + Weeks_15 = 207, + Weeks_16 = 208, + Weeks_17 = 209, + Weeks_18 = 210, + Weeks_19 = 211, + Weeks_20 = 212, + Weeks_21 = 213, + Weeks_22 = 214, + Weeks_23 = 215, + Weeks_24 = 216, + Weeks_25 = 217, + Weeks_26 = 218, + Weeks_27 = 219, + Weeks_28 = 220, + Weeks_29 = 221, + Weeks_30 = 222, + Weeks_31 = 223, + Weeks_32 = 224, + Weeks_33 = 225, + Weeks_34 = 226, + Weeks_35 = 227, + Weeks_36 = 228, + Weeks_37 = 229, + Weeks_38 = 230, + Weeks_39 = 231, + Weeks_40 = 232, + Weeks_41 = 233, + Weeks_42 = 234, + Weeks_43 = 235, + Weeks_44 = 236, + Weeks_45 = 237, + Weeks_46 = 238, + Weeks_47 = 239, + Weeks_48 = 240, + Weeks_49 = 241, + Weeks_50 = 242, + Weeks_51 = 243, + Weeks_52 = 244, + Weeks_53 = 245, + Weeks_54 = 246, + Weeks_55 = 247, + Weeks_56 = 248, + Weeks_57 = 249, + Weeks_58 = 250, + Weeks_59 = 251, + Weeks_60 = 252, + Weeks_61 = 253, + Weeks_62 = 254, + Weeks_63 = 255 + } } diff --git a/src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs b/src/HeboTech.ATLib/Events/SmsStorageReferenceReceivedEventArgs.cs similarity index 72% rename from src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs rename to src/HeboTech.ATLib/Events/SmsStorageReferenceReceivedEventArgs.cs index b91a75d..5b2a591 100644 --- a/src/HeboTech.ATLib/Events/SmsReceivedStorageReferenceEventArgs.cs +++ b/src/HeboTech.ATLib/Events/SmsStorageReferenceReceivedEventArgs.cs @@ -2,9 +2,9 @@ namespace HeboTech.ATLib.Events { - public class SmsReceivedStorageReferenceEventArgs + public class SmsStorageReferenceReceivedEventArgs { - public SmsReceivedStorageReferenceEventArgs(string storage, int index) + public SmsStorageReferenceReceivedEventArgs(string storage, int index) { Storage = storage; Index = index; @@ -13,14 +13,14 @@ public SmsReceivedStorageReferenceEventArgs(string storage, int index) public string Storage { get; } public int Index { get; } - public static SmsReceivedStorageReferenceEventArgs CreateFromResponse(string response) + public static SmsStorageReferenceReceivedEventArgs CreateFromResponse(string response) { var match = Regex.Match(response, @"\+CMTI:\s""(?[A-Z]+)"",(?\d+)"); if (match.Success) { string storage = match.Groups["storage"].Value; int index = int.Parse(match.Groups["index"].Value); - return new SmsReceivedStorageReferenceEventArgs(storage, index); + return new SmsStorageReferenceReceivedEventArgs(storage, index); } return default; } diff --git a/src/HeboTech.ATLib/Extensions/SmsDeliverExtensions.cs b/src/HeboTech.ATLib/Extensions/SmsDeliverExtensions.cs deleted file mode 100644 index 524cf1c..0000000 --- a/src/HeboTech.ATLib/Extensions/SmsDeliverExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using HeboTech.ATLib.DTOs; - -namespace HeboTech.ATLib.Extensions -{ - internal static class SmsDeliverExtensions - { - public static Sms ToSms(this SmsDeliver sms, SmsStatus status) - { - return new Sms(status, sms.SenderNumber, sms.Timestamp, sms.Message, sms.MessageReferenceNumber, sms.TotalNumberOfParts, sms.PartNumber); - } - - public static SmsWithIndex ToSmsWithIndex(this SmsDeliver sms, int index, SmsStatus status) - { - return new SmsWithIndex(index, status, sms.SenderNumber, sms.Timestamp, sms.Message, sms.MessageReferenceNumber, sms.TotalNumberOfParts, sms.PartNumber); - } - } -} diff --git a/src/HeboTech.ATLib/Extensions/SmsExtensions.cs b/src/HeboTech.ATLib/Extensions/SmsExtensions.cs new file mode 100644 index 0000000..111a0f0 --- /dev/null +++ b/src/HeboTech.ATLib/Extensions/SmsExtensions.cs @@ -0,0 +1,12 @@ +using HeboTech.ATLib.DTOs; + +namespace HeboTech.ATLib.Extensions +{ + internal static class SmsExtensions + { + public static SmsBaseWithIndex ToSmsWithIndex(this SmsBase sms, int index) + { + return new SmsBaseWithIndex(sms, index); + } + } +} diff --git a/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs b/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs index dbb567c..1d88ea0 100644 --- a/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs +++ b/src/HeboTech.ATLib/Modems/Cinterion/MC55i.cs @@ -1,5 +1,4 @@ -using HeboTech.ATLib.CodingSchemes; -using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.DTOs; using HeboTech.ATLib.Modems.Generic; using HeboTech.ATLib.Parsers; using HeboTech.ATLib.PDU; @@ -26,48 +25,12 @@ public MC55i(IAtChannel channel) { } - public async Task>> SendSmsAsync(PhoneNumber phoneNumber, string message) + public override async Task>> SendSmsAsync(SmsSubmitRequest request) { - if (phoneNumber is null) - throw new ArgumentNullException(nameof(phoneNumber)); - if (message is null) - throw new ArgumentNullException(nameof(message)); + if (request is null) + throw new ArgumentNullException(nameof(request)); - IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message) { IncludeEmptySmscLength = true, EnableStatusReportRequest = true }); - List> references = new List>(); - foreach (string pdu in pdus) - { - string cmd1 = $"AT+CMGS={(pdu.Length - 2) / 2}"; // Subtract 2 (one octet) for SMSC. - string cmd2 = pdu; - AtResponse response = await channel.SendSmsAsync(cmd1, cmd2, "+CMGS:", TimeSpan.FromSeconds(30)); - - if (response.Success) - { - string line = response.Intermediates.First(); - var match = Regex.Match(line, @"\+CMGS:\s(?\d+)"); - if (match.Success) - { - int mr = int.Parse(match.Groups["mr"].Value); - references.Add(ModemResponse.IsResultSuccess(new SmsReference(mr))); - } - } - else - { - if (AtErrorParsers.TryGetError(response.FinalResponse, out Error error)) - references.Add(ModemResponse.HasResultError(error)); - } - } - return references; - } - - public async Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - { - if (phoneNumber is null) - throw new ArgumentNullException(nameof(phoneNumber)); - if (message is null) - throw new ArgumentNullException(nameof(message)); - - IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message, codingScheme) { IncludeEmptySmscLength = true }); + IEnumerable pdus = SmsSubmitEncoder.Encode(request, true); List> references = new List>(); foreach (string pdu in pdus) { @@ -134,5 +97,25 @@ public async Task> MC55i_GetBatteryStatusAsync AtErrorParsers.TryGetError(response.FinalResponse, out Error error); return ModemResponse.HasResultError(error); } + + /// + /// Sets how receiving a new SMS is indicated + /// + /// mode + /// mt + /// bm + /// ds + /// Not in use + /// Command status + public override async Task SetNewSmsIndicationAsync(int mode, int mt, int bm, int ds, int bfr) + { + AtResponse response = await channel.SendCommand($"AT+CNMI={mode},{mt},{bm},{ds}"); + + if (response.Success) + return ModemResponse.IsSuccess(); + + AtErrorParsers.TryGetError(response.FinalResponse, out Error error); + return ModemResponse.HasError(error); + } } } diff --git a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs index 11fdec4..3055ecb 100644 --- a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs +++ b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs @@ -26,7 +26,7 @@ public ModemBase(IAtChannel channel) channel.UnsolicitedEvent += Channel_UnsolicitedEvent; } - private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) + protected virtual void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) { if (e.Line1 == "RING") IncomingCall?.Invoke(this, new IncomingCallEventArgs()); @@ -40,7 +40,7 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) else if (e.Line1.StartsWith("+CMT: ")) SmsReceived?.Invoke(this, SmsReceivedEventArgs.CreateFromResponse(e.Line1, e.Line2)); else if (e.Line1.StartsWith("+CMTI: ")) - SmsStorageReferenceReceived?.Invoke(this, SmsReceivedStorageReferenceEventArgs.CreateFromResponse(e.Line1)); + SmsStorageReferenceReceived?.Invoke(this, SmsStorageReferenceReceivedEventArgs.CreateFromResponse(e.Line1)); else if (e.Line1.StartsWith("+CBM: ")) BroadcastMessageReceived?.Invoke(this, BreadcastMessageReceivedEventArgs.CreateFromResponse(e.Line1, e.Line2)); @@ -66,7 +66,7 @@ private void Channel_UnsolicitedEvent(object sender, UnsolicitedEventArgs e) public event EventHandler GenericEvent; public event EventHandler SmsReceived; - public event EventHandler SmsStorageReferenceReceived; + public event EventHandler SmsStorageReferenceReceived; public event EventHandler BroadcastMessageReceived; public event EventHandler BroadcastMessageStorageReferenceReceived; @@ -241,6 +241,17 @@ public virtual async Task SetSmsMessageFormatAsync(SmsTextFormat return ModemResponse.HasError(error); } + public virtual async Task SetSelectMessageService(int service) + { + AtResponse response = await channel.SendCommand($"AT+CSMS={service}"); + + if (response.Success) + return ModemResponse.IsSuccess(); + + AtErrorParsers.TryGetError(response.FinalResponse, out Error error); + return ModemResponse.HasError(error); + } + public virtual async Task SetNewSmsIndicationAsync(int mode, int mt, int bm, int ds, int bfr) { AtResponse response = await channel.SendCommand($"AT+CNMI={mode},{mt},{bm},{ds},{bfr}"); @@ -252,20 +263,17 @@ public virtual async Task SetNewSmsIndicationAsync(int mode, int return ModemResponse.HasError(error); } - protected virtual Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, bool includeEmptySmscLength) + public virtual Task>> SendSmsAsync(SmsSubmitRequest request) { - CharacterSet characterSet = Gsm7.IsGsm7Compatible(message.ToCharArray()) ? CharacterSet.Gsm7 : CharacterSet.UCS2; - return SendSmsAsync(phoneNumber, message, characterSet, includeEmptySmscLength); + return SendSmsAsync(request, true); } - protected virtual async Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme, bool includeEmptySmscLength) + protected async Task>> SendSmsAsync(SmsSubmitRequest request, bool includeEmptySmscLength) { - if (phoneNumber is null) - throw new ArgumentNullException(nameof(phoneNumber)); - if (message is null) - throw new ArgumentNullException(nameof(message)); + if (request is null) + throw new ArgumentNullException(nameof(request)); - IEnumerable pdus = SmsSubmitEncoder.Encode(new SmsSubmitRequest(phoneNumber, message, codingScheme) { IncludeEmptySmscLength = includeEmptySmscLength, EnableStatusReportRequest = true }); + IEnumerable pdus = SmsSubmitEncoder.Encode(request, includeEmptySmscLength); List> references = new List>(); foreach (string pdu in pdus) { @@ -367,14 +375,14 @@ public virtual async Task> SetPreferredM return ModemResponse.HasResultError(error); } - public virtual async Task> ReadSmsAsync(int index) + public virtual async Task> ReadSmsAsync(int index) { AtResponse pduResponse = await channel.SendMultilineCommand($"AT+CMGR={index}", null); if (pduResponse.Success) { if (!pduResponse.Intermediates.Any()) - return ModemResponse.HasResultError(); + return ModemResponse.HasResultError(); string line1 = pduResponse.Intermediates[0]; var line1Match = Regex.Match(line1, @"\+CMGR:\s(?\d),(""(?\w*)"")*,(?\d+)"); @@ -387,33 +395,32 @@ public virtual async Task> ReadSmsAsync(int index) if (length > 0) { string line2 = pduResponse.Intermediates[1]; - var line2Match = Regex.Match(line2, @"(?[0-9A-Z]*)"); + var line2Match = Regex.Match(line2, @"(?[0-9A-Z]*)"); if (line2Match.Success) { - string pdu = line2Match.Groups["status"].Value; - SmsDeliver pduMessage = SmsDeliverDecoder.Decode(pdu.ToByteArray()); - - return ModemResponse.IsResultSuccess(pduMessage.ToSms(status)); + string pduString = line2Match.Groups["pdu"].Value; + SmsBase sms = SmsDecoder.Decode(pduString.ToByteArray(), status); + return ModemResponse.IsResultSuccess(sms); } } } } AtErrorParsers.TryGetError(pduResponse.FinalResponse, out Error pduError); - return ModemResponse.HasResultError(pduError); + return ModemResponse.HasResultError(pduError); } - public virtual async Task>> ListSmssAsync(SmsStatus smsStatus) + public virtual async Task>> ListSmssAsync(SmsStatus smsStatus) { string command = $"AT+CMGL={(int)smsStatus}"; AtResponse response = await channel.SendMultilineCommand(command, null); - List smss = new List(); + List smss = new List(); if (response.Success) { if ((response.Intermediates.Count % 2) != 0) - return ModemResponse.HasResultError>(); + return ModemResponse.HasResultError>(); for (int i = 0; i < response.Intermediates.Count; i += 2) { @@ -428,8 +435,8 @@ public virtual async Task>> ListSmssAsync(SmsSt // Sent when AT+CSDH=1 is set int length = int.Parse(match.Groups["length"].Value); - SmsDeliver sms = SmsDeliverDecoder.Decode(messageLine.ToByteArray()); - smss.Add(sms.ToSmsWithIndex(index, status)); + SmsBase sms = SmsDecoder.Decode(messageLine.ToByteArray(), status); + smss.Add(sms.ToSmsWithIndex(index)); } } } diff --git a/src/HeboTech.ATLib/Modems/IModem.cs b/src/HeboTech.ATLib/Modems/IModem.cs index c5ceed5..39e951a 100644 --- a/src/HeboTech.ATLib/Modems/IModem.cs +++ b/src/HeboTech.ATLib/Modems/IModem.cs @@ -39,7 +39,7 @@ public interface IModem : IDisposable /// /// Indicates that an SMS is received /// - event EventHandler SmsStorageReferenceReceived; + event EventHandler SmsStorageReferenceReceived; /// /// Indicates that a USSD response is received @@ -162,7 +162,7 @@ public interface IModem : IDisposable /// /// /// Command status with a list of SMSs - Task>> ListSmssAsync(SmsStatus smsStatus); + Task>> ListSmssAsync(SmsStatus smsStatus); /// /// Set preferred message storages @@ -190,7 +190,7 @@ public interface IModem : IDisposable /// /// /// Command status with SMS - Task> ReadSmsAsync(int index); + Task> ReadSmsAsync(int index); /// /// Resets the modem to factory defaults @@ -198,22 +198,12 @@ public interface IModem : IDisposable /// Command status Task ResetToFactoryDefaultsAsync(); - /// - /// Sends an SMS in PDU format. This will automatically select the Data Coding Scheme that will result in the fewest messages being sent in case of a concatenated SMS based on the content of the message. - /// - /// The number to send to - /// The message body - /// Command status with SMS reference - Task>> SendSmsAsync(PhoneNumber phoneNumber, string message); - /// /// Sends an SMS in PDU format /// - /// The number to send to - /// The message body - /// Encoding to use + /// The SMS request /// Command status with SMS reference - Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme = CharacterSet.UCS2); + Task>> SendSmsAsync(SmsSubmitRequest request); /// /// Sends an USSD code. Results in an UssdResponseReceived event @@ -244,6 +234,13 @@ public interface IModem : IDisposable /// Command status Task SetErrorFormatAsync(int errorFormat); + /// + /// Select Message Service + /// + /// Typical: 0, 1 + /// Command status + Task SetSelectMessageService(int service); + /// /// Sets how receiving a new SMS is indicated /// diff --git a/src/HeboTech.ATLib/Modems/Qualcomm/MDM9225.cs b/src/HeboTech.ATLib/Modems/Qualcomm/MDM9225.cs index af2d81e..6a85e7f 100644 --- a/src/HeboTech.ATLib/Modems/Qualcomm/MDM9225.cs +++ b/src/HeboTech.ATLib/Modems/Qualcomm/MDM9225.cs @@ -1,5 +1,4 @@ -using HeboTech.ATLib.CodingSchemes; -using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.DTOs; using HeboTech.ATLib.Modems.Generic; using HeboTech.ATLib.Parsers; using System.Collections.Generic; @@ -14,14 +13,9 @@ public MDM9225(IAtChannel channel) { } - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message) + public override Task>> SendSmsAsync(SmsSubmitRequest request) { - return base.SendSmsAsync(phoneNumber, message, false); - } - - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - { - return base.SendSmsAsync(phoneNumber, message, codingScheme, false); + return SendSmsAsync(request, false); } } } diff --git a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs index 62ba9cd..59cce99 100644 --- a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs +++ b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs @@ -1,5 +1,4 @@ -using HeboTech.ATLib.CodingSchemes; -using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.DTOs; using HeboTech.ATLib.Extensions; using HeboTech.ATLib.Modems.Generic; using HeboTech.ATLib.Parsers; @@ -42,27 +41,22 @@ public virtual async Task GetRemainingPinPukAttemptsAsy #region _3GPP_TS_27_005 - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message) + public override Task>> SendSmsAsync(SmsSubmitRequest request) { - return base.SendSmsAsync(phoneNumber, message, false); + return SendSmsAsync(request, false); } - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - { - return base.SendSmsAsync(phoneNumber, message, codingScheme, false); - } - - public override async Task>> ListSmssAsync(SmsStatus smsStatus) + public override async Task>> ListSmssAsync(SmsStatus smsStatus) { string command = $"AT+CMGL={(int)smsStatus}"; AtResponse response = await channel.SendMultilineCommand(command, null); - List smss = new List(); + List smss = new List(); if (response.Success) { if ((response.Intermediates.Count % 2) != 0) - return ModemResponse.HasResultError>(); + return ModemResponse.HasResultError>(); for (int i = 0; i < response.Intermediates.Count; i += 2) { @@ -77,8 +71,8 @@ public override async Task>> ListSmssAsync(SmsS // Sent when AT+CSDH=1 is set int length = int.Parse(match.Groups["length"].Value); - SmsDeliver sms = SmsDeliverDecoder.Decode(messageLine.ToByteArray()); - smss.Add(new SmsWithIndex(index, status, sms.SenderNumber, sms.Timestamp, sms.Message)); + SmsBase sms = SmsDeliverDecoder.Decode(messageLine.ToByteArray()); + smss.Add(sms.ToSmsWithIndex(index)); } } } diff --git a/src/HeboTech.ATLib/Modems/TP-LINK/MA260.cs b/src/HeboTech.ATLib/Modems/TP-LINK/MA260.cs index dd79989..803109a 100644 --- a/src/HeboTech.ATLib/Modems/TP-LINK/MA260.cs +++ b/src/HeboTech.ATLib/Modems/TP-LINK/MA260.cs @@ -1,5 +1,4 @@ -using HeboTech.ATLib.CodingSchemes; -using HeboTech.ATLib.DTOs; +using HeboTech.ATLib.DTOs; using HeboTech.ATLib.Modems.Generic; using HeboTech.ATLib.Parsers; using System.Collections.Generic; @@ -20,14 +19,9 @@ public MA260(IAtChannel channel) { } - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message) + public override Task>> SendSmsAsync(SmsSubmitRequest request) { - return base.SendSmsAsync(phoneNumber, message, false); - } - - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - { - return base.SendSmsAsync(phoneNumber, message, codingScheme, false); + return SendSmsAsync(request, false); } } } diff --git a/src/HeboTech.ATLib/Modems/Telit/ME910C1.cs b/src/HeboTech.ATLib/Modems/Telit/ME910C1.cs index f6b47f6..54d2708 100644 --- a/src/HeboTech.ATLib/Modems/Telit/ME910C1.cs +++ b/src/HeboTech.ATLib/Modems/Telit/ME910C1.cs @@ -31,14 +31,9 @@ public override async Task SetRequiredSettingsAfterPinAsync() return currentCharacterSet.Success && smsMessageFormat.Success; } - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message) + public override Task>> SendSmsAsync(SmsSubmitRequest request) { - return base.SendSmsAsync(phoneNumber, message, false); - } - - public Task>> SendSmsAsync(PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - { - return base.SendSmsAsync(phoneNumber, message, codingScheme, false); + return SendSmsAsync(request, false); } } } diff --git a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs index c5f625b..782ff34 100644 --- a/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs +++ b/src/HeboTech.ATLib/PDU/MessageTypeIndicator.cs @@ -6,14 +6,15 @@ /// SC: SMS Center /// MS: Mobile Station /// - internal enum MessageTypeIndicatorInbound : byte + public enum MessageTypeIndicatorInbound : byte { // SC -> MS - SMS_DELIVER_REPORT = 0x00, + SMS_DELIVER = 0x00, // SC -> MS SMS_SUBMIT_REPORT = 0x01, // SC -> MS SMS_STATUS_REPORT = 0x02, + Reserved = 0x03 } @@ -26,11 +27,12 @@ internal enum MessageTypeIndicatorInbound : byte internal enum MessageTypeIndicatorOutbound : byte { // MS -> SC - SMS_DELIVER = 0x00, + SMS_DELIVER_REPORT = 0x00, // MS -> SC SMS_SUBMIT = 0x01, // MS -> SC SMS_COMMAND = 0x02, + Reserved = 0x03 } } diff --git a/src/HeboTech.ATLib/PDU/ReceivedMessageTypeParser.cs b/src/HeboTech.ATLib/PDU/ReceivedMessageTypeParser.cs new file mode 100644 index 0000000..0b06090 --- /dev/null +++ b/src/HeboTech.ATLib/PDU/ReceivedMessageTypeParser.cs @@ -0,0 +1,18 @@ +using System; + +namespace HeboTech.ATLib.PDU +{ + internal static class ReceivedMessageTypeParser + { + public static MessageTypeIndicatorInbound Parse(ReadOnlySpan bytes) + { + byte smsc_length = bytes[0]; // Get length of SMSC + byte headerByte = bytes[1 + smsc_length]; // Skip over SMSC length byte and SMSC to get to header + + byte mti = (byte)(headerByte & 0b0000_0011); + if (Enum.IsDefined(typeof(MessageTypeIndicatorInbound), mti)) + return (MessageTypeIndicatorInbound)mti; + throw new ArgumentOutOfRangeException(nameof(mti)); + } + } +} diff --git a/src/HeboTech.ATLib/PDU/SmsDecoder.cs b/src/HeboTech.ATLib/PDU/SmsDecoder.cs new file mode 100644 index 0000000..f9701e7 --- /dev/null +++ b/src/HeboTech.ATLib/PDU/SmsDecoder.cs @@ -0,0 +1,41 @@ +using HeboTech.ATLib.DTOs; +using System; + +namespace HeboTech.ATLib.PDU +{ + internal class SmsDecoder + { + public static SmsBase Decode(ReadOnlySpan bytes, SmsStatus status, int timestampYearOffset = 2000) + { + int offset = 0; + + // SMSC information + byte smsc_length = bytes[offset++]; + PhoneNumberDTO serviceCenterNumber = null; + if (smsc_length > 0) + { + serviceCenterNumber = PhoneNumberDecoder.DecodePhoneNumber(bytes[offset..(offset += smsc_length)]); + } + + // Header + byte headerByte = bytes[offset++]; + + MessageTypeIndicatorInbound mti = (MessageTypeIndicatorInbound)(headerByte & 0b0000_0011); + switch (mti) + { + case MessageTypeIndicatorInbound.SMS_DELIVER: + return SmsDeliverDecoder.Decode(bytes, timestampYearOffset); + case MessageTypeIndicatorInbound.SMS_SUBMIT_REPORT: + break; + case MessageTypeIndicatorInbound.SMS_STATUS_REPORT: + return SmsStatusReportDecoder.Decode(bytes, timestampYearOffset); + case MessageTypeIndicatorInbound.Reserved: + break; + default: + break; + } + + throw new NotImplementedException(); + } + } +} diff --git a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs index d858739..05deee7 100644 --- a/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsDeliverDecoder.cs @@ -13,7 +13,7 @@ private SmsDeliverHeader() { } - public SmsDeliverHeader(MessageTypeIndicatorOutbound mti, bool mms, bool lp, bool sri, bool udhi, bool rp) + public SmsDeliverHeader(MessageTypeIndicatorInbound mti, bool mms, bool lp, bool sri, bool udhi, bool rp) { MTI = mti; MMS = mms; @@ -23,7 +23,7 @@ public SmsDeliverHeader(MessageTypeIndicatorOutbound mti, bool mms, bool lp, boo RP = rp; } - public MessageTypeIndicatorOutbound MTI { get; private set; } + public MessageTypeIndicatorInbound MTI { get; private set; } public bool MMS { get; private set; } public bool LP { get; private set; } public bool SRI { get; private set; } @@ -34,8 +34,8 @@ public static SmsDeliverHeader Parse(byte header) { SmsDeliverHeader parsedHeader = new SmsDeliverHeader(); - parsedHeader.MTI = (MessageTypeIndicatorOutbound)(header & 0b0000_0011); - if (parsedHeader.MTI != (byte)MessageTypeIndicatorOutbound.SMS_DELIVER) + parsedHeader.MTI = (MessageTypeIndicatorInbound)(header & 0b0000_0011); + if (parsedHeader.MTI != (byte)MessageTypeIndicatorInbound.SMS_DELIVER) throw new ArgumentException("Invalid SMS-DELIVER data"); parsedHeader.MMS = (header & (1 << 2)) != 0; diff --git a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs index 75c4bad..ffda24c 100644 --- a/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsSubmitEncoder.cs @@ -32,8 +32,8 @@ internal class SmsSubmitEncoder protected byte pi; // TP-DCS Data Coding Scheme. '00'-7bit default alphabet. '04'-8bit protected CharacterSet dcs; - // TP-Validity-Period. 'AA'-4 days - protected List vp = new List(); + // TP-Validity-Period + protected ValidityPeriod validityPeriod = null; // Message protected Message partitionedMessage; @@ -52,7 +52,7 @@ protected static SmsSubmitEncoder Initialize() /// /// Data object /// PDUs - public static IEnumerable Encode(SmsSubmitRequest smsSubmit) + public static IEnumerable Encode(SmsSubmitRequest smsSubmit, bool includeEmptySmscLength) { // Build TPDU var messageParts = SmsSubmitEncoder @@ -68,7 +68,7 @@ public static IEnumerable Encode(SmsSubmitRequest smsSubmit) StringBuilder sb = new StringBuilder(); // Length of SMSC information - if (smsSubmit.IncludeEmptySmscLength) + if (includeEmptySmscLength) sb.Append("00"); sb.Append(messagePart); @@ -136,8 +136,7 @@ protected SmsSubmitEncoder ValidityPeriod(ValidityPeriod validityPeriod) header |= (byte)((byte)validityPeriod.Format << 3); // Set value - vp.Clear(); - vp.AddRange(validityPeriod.Value); + this.validityPeriod = validityPeriod; return this; } @@ -215,8 +214,8 @@ protected IEnumerable Build() sb.Append(daNumber); sb.Append(pi.ToString("X2")); sb.Append(((byte)dcs).ToString("X2")); - if (vp.Count > 0) - sb.Append(String.Join("", vp.Select(x => x.ToString("X2")))); + if (validityPeriod != null) + sb.Append(String.Join("", validityPeriod.Value.Select(x => x.ToString("X2")))); switch (dcs) { From eeac486485a4b5b6f554e7d22b51fb8eea508760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Thu, 2 May 2024 20:45:55 +0200 Subject: [PATCH 4/6] Sms refactor --- .../FunctionalityTest.cs | 2 +- src/HeboTech.ATLib/DTOs/Sms.cs | 30 +++++++------------ src/HeboTech.ATLib/DTOs/SmsBase.cs | 26 ---------------- src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs | 14 --------- src/HeboTech.ATLib/DTOs/SmsDeliver.cs | 2 +- src/HeboTech.ATLib/DTOs/SmsStatusReport.cs | 2 +- src/HeboTech.ATLib/DTOs/SmsWithIndex.cs | 24 ++++----------- .../Extensions/SmsExtensions.cs | 4 +-- .../Modems/Generic/ModemBase.cs | 16 +++++----- src/HeboTech.ATLib/Modems/IModem.cs | 4 +-- src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs | 8 ++--- src/HeboTech.ATLib/PDU/SmsDecoder.cs | 2 +- 12 files changed, 36 insertions(+), 98 deletions(-) delete mode 100644 src/HeboTech.ATLib/DTOs/SmsBase.cs delete mode 100644 src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs diff --git a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs index 0255aa5..145fa96 100644 --- a/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs +++ b/src/HeboTech.ATLib.TestConsole/FunctionalityTest.cs @@ -106,7 +106,7 @@ public static async Task RunAsync(System.IO.Stream stream, string pin) var selectMessageService = await modem.SetSelectMessageService(0); Console.WriteLine($"Setting select message service: {selectMessageService}"); - var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 2, 0); // 2, 2, 2, 1, 1 -> PDU (2, 1, 2, 1, 0) + var newSmsIndicationResult = await modem.SetNewSmsIndicationAsync(2, 1, 0, 2, 0); // 2, 1, 0, 2, 0 (CSMS=0) Console.WriteLine($"Setting new SMS indication: {newSmsIndicationResult}"); var supportedStorages = await modem.GetSupportedPreferredMessageStoragesAsync(); diff --git a/src/HeboTech.ATLib/DTOs/Sms.cs b/src/HeboTech.ATLib/DTOs/Sms.cs index 4fafdc6..368cae7 100644 --- a/src/HeboTech.ATLib/DTOs/Sms.cs +++ b/src/HeboTech.ATLib/DTOs/Sms.cs @@ -1,36 +1,26 @@ -using System; +using HeboTech.ATLib.PDU; namespace HeboTech.ATLib.DTOs { public class Sms { - public Sms(SmsStatus status, PhoneNumberDTO sender, DateTimeOffset receiveTime, string message) - : this(status, sender, receiveTime, message, 0, 1, 1) + protected Sms(MessageTypeIndicatorInbound messageTypeIndicator) { + MessageTypeIndicator = messageTypeIndicator; } - public Sms(SmsStatus status, PhoneNumberDTO sender, DateTimeOffset receiveTime, string message, int messageReferenceNumber, int totalNumberOfParts, int partNumber) + protected Sms(MessageTypeIndicatorInbound messageTypeIndicator, int messageReference) + : this(messageTypeIndicator) { - Status = status; - Sender = sender; - ReceiveTime = receiveTime; - Message = message; - MessageReferenceNumber = messageReferenceNumber; - TotalNumberOfParts = totalNumberOfParts; - PartNumber = partNumber; + MessageReference = messageReference; } - public SmsStatus Status { get; } - public PhoneNumberDTO Sender { get; } - public DateTimeOffset ReceiveTime { get;} - public string Message { get; } - public int MessageReferenceNumber { get; } - public int TotalNumberOfParts { get; } - public int PartNumber { get; } + public int MessageReference { get; } + public MessageTypeIndicatorInbound MessageTypeIndicator { get; } public override string ToString() { - return $"Sender:\t\t{Sender}{Environment.NewLine}ReceiveTime:\t{ReceiveTime}{Environment.NewLine}Ref. no.:\t{MessageReferenceNumber}{Environment.NewLine}Part:\t\t{PartNumber}/{TotalNumberOfParts}{Environment.NewLine}Message:\t{Message}"; + return $"MTI: {MessageTypeIndicator}, Msg. ref.: {MessageReference}"; } } -} +} \ No newline at end of file diff --git a/src/HeboTech.ATLib/DTOs/SmsBase.cs b/src/HeboTech.ATLib/DTOs/SmsBase.cs deleted file mode 100644 index 3bd602b..0000000 --- a/src/HeboTech.ATLib/DTOs/SmsBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using HeboTech.ATLib.PDU; - -namespace HeboTech.ATLib.DTOs -{ - public class SmsBase - { - protected SmsBase(MessageTypeIndicatorInbound messageTypeIndicator) - { - MessageTypeIndicator = messageTypeIndicator; - } - - protected SmsBase(MessageTypeIndicatorInbound messageTypeIndicator, int messageReference) - : this(messageTypeIndicator) - { - MessageReference = messageReference; - } - - public int MessageReference { get; } - public MessageTypeIndicatorInbound MessageTypeIndicator { get; } - - public override string ToString() - { - return $"MTI: {MessageTypeIndicator}, Msg. ref.: {MessageReference}"; - } - } -} \ No newline at end of file diff --git a/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs b/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs deleted file mode 100644 index e572d4a..0000000 --- a/src/HeboTech.ATLib/DTOs/SmsBaseWithIndex.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace HeboTech.ATLib.DTOs -{ - public class SmsBaseWithIndex - { - public SmsBaseWithIndex(SmsBase sms, int index) - { - Sms = sms; - Index = index; - } - - public SmsBase Sms { get; } - public int Index { get; } - } -} \ No newline at end of file diff --git a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs index 07f5a7d..ef7e3d0 100644 --- a/src/HeboTech.ATLib/DTOs/SmsDeliver.cs +++ b/src/HeboTech.ATLib/DTOs/SmsDeliver.cs @@ -6,7 +6,7 @@ namespace HeboTech.ATLib.DTOs /// /// Data object for a received SMS /// - public class SmsDeliver : SmsBase + public class SmsDeliver : Sms { public SmsDeliver(PhoneNumberDTO serviceCenterNumber, PhoneNumberDTO senderNumber, string message, DateTimeOffset timestamp) : base(MessageTypeIndicatorInbound.SMS_DELIVER) diff --git a/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs index a250581..ce64d0f 100644 --- a/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs +++ b/src/HeboTech.ATLib/DTOs/SmsStatusReport.cs @@ -3,7 +3,7 @@ namespace HeboTech.ATLib.DTOs { - public class SmsStatusReport : SmsBase + public class SmsStatusReport : Sms { public SmsStatusReport(int messageReference, PhoneNumberDTO recipientAddress, DateTimeOffset serviceCenterTimestamp, DateTimeOffset dischargeTime, SmsDeliveryStatus status) : base(MessageTypeIndicatorInbound.SMS_STATUS_REPORT, messageReference) diff --git a/src/HeboTech.ATLib/DTOs/SmsWithIndex.cs b/src/HeboTech.ATLib/DTOs/SmsWithIndex.cs index fee9dbe..ee387fa 100644 --- a/src/HeboTech.ATLib/DTOs/SmsWithIndex.cs +++ b/src/HeboTech.ATLib/DTOs/SmsWithIndex.cs @@ -1,26 +1,14 @@ -using System; - -namespace HeboTech.ATLib.DTOs +namespace HeboTech.ATLib.DTOs { - public class SmsWithIndex : Sms + public class SmsWithIndex { - public SmsWithIndex(int index, SmsStatus status, PhoneNumberDTO sender, DateTimeOffset receiveTime, string message) - : base(status, sender, receiveTime, message) - { - Index = index; - } - - public SmsWithIndex(int index, SmsStatus status, PhoneNumberDTO sender, DateTimeOffset receiveTime, string message, int messageReferenceNumber, int totalNumberOfParts, int partNumber) - : base(status, sender, receiveTime, message, messageReferenceNumber, totalNumberOfParts, partNumber) + public SmsWithIndex(Sms sms, int index) { + Sms = sms; Index = index; } + public Sms Sms { get; } public int Index { get; } - - public override string ToString() - { - return $"Index:\t\t{Index}{Environment.NewLine}" + base.ToString(); - } } -} +} \ No newline at end of file diff --git a/src/HeboTech.ATLib/Extensions/SmsExtensions.cs b/src/HeboTech.ATLib/Extensions/SmsExtensions.cs index 111a0f0..f06d930 100644 --- a/src/HeboTech.ATLib/Extensions/SmsExtensions.cs +++ b/src/HeboTech.ATLib/Extensions/SmsExtensions.cs @@ -4,9 +4,9 @@ namespace HeboTech.ATLib.Extensions { internal static class SmsExtensions { - public static SmsBaseWithIndex ToSmsWithIndex(this SmsBase sms, int index) + public static SmsWithIndex ToSmsWithIndex(this Sms sms, int index) { - return new SmsBaseWithIndex(sms, index); + return new SmsWithIndex(sms, index); } } } diff --git a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs index 3055ecb..209f053 100644 --- a/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs +++ b/src/HeboTech.ATLib/Modems/Generic/ModemBase.cs @@ -375,14 +375,14 @@ public virtual async Task> SetPreferredM return ModemResponse.HasResultError(error); } - public virtual async Task> ReadSmsAsync(int index) + public virtual async Task> ReadSmsAsync(int index) { AtResponse pduResponse = await channel.SendMultilineCommand($"AT+CMGR={index}", null); if (pduResponse.Success) { if (!pduResponse.Intermediates.Any()) - return ModemResponse.HasResultError(); + return ModemResponse.HasResultError(); string line1 = pduResponse.Intermediates[0]; var line1Match = Regex.Match(line1, @"\+CMGR:\s(?\d),(""(?\w*)"")*,(?\d+)"); @@ -399,7 +399,7 @@ public virtual async Task> ReadSmsAsync(int index) if (line2Match.Success) { string pduString = line2Match.Groups["pdu"].Value; - SmsBase sms = SmsDecoder.Decode(pduString.ToByteArray(), status); + Sms sms = SmsDecoder.Decode(pduString.ToByteArray(), status); return ModemResponse.IsResultSuccess(sms); } } @@ -407,20 +407,20 @@ public virtual async Task> ReadSmsAsync(int index) } AtErrorParsers.TryGetError(pduResponse.FinalResponse, out Error pduError); - return ModemResponse.HasResultError(pduError); + return ModemResponse.HasResultError(pduError); } - public virtual async Task>> ListSmssAsync(SmsStatus smsStatus) + public virtual async Task>> ListSmssAsync(SmsStatus smsStatus) { string command = $"AT+CMGL={(int)smsStatus}"; AtResponse response = await channel.SendMultilineCommand(command, null); - List smss = new List(); + List smss = new List(); if (response.Success) { if ((response.Intermediates.Count % 2) != 0) - return ModemResponse.HasResultError>(); + return ModemResponse.HasResultError>(); for (int i = 0; i < response.Intermediates.Count; i += 2) { @@ -435,7 +435,7 @@ public virtual async Task>> ListSmssAsync(S // Sent when AT+CSDH=1 is set int length = int.Parse(match.Groups["length"].Value); - SmsBase sms = SmsDecoder.Decode(messageLine.ToByteArray(), status); + Sms sms = SmsDecoder.Decode(messageLine.ToByteArray(), status); smss.Add(sms.ToSmsWithIndex(index)); } } diff --git a/src/HeboTech.ATLib/Modems/IModem.cs b/src/HeboTech.ATLib/Modems/IModem.cs index 39e951a..20695d1 100644 --- a/src/HeboTech.ATLib/Modems/IModem.cs +++ b/src/HeboTech.ATLib/Modems/IModem.cs @@ -162,7 +162,7 @@ public interface IModem : IDisposable /// /// /// Command status with a list of SMSs - Task>> ListSmssAsync(SmsStatus smsStatus); + Task>> ListSmssAsync(SmsStatus smsStatus); /// /// Set preferred message storages @@ -190,7 +190,7 @@ public interface IModem : IDisposable /// /// /// Command status with SMS - Task> ReadSmsAsync(int index); + Task> ReadSmsAsync(int index); /// /// Resets the modem to factory defaults diff --git a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs index 59cce99..04c4328 100644 --- a/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs +++ b/src/HeboTech.ATLib/Modems/SIMCOM/SIM5320.cs @@ -46,17 +46,17 @@ public override Task>> SendSmsAsync(SmsS return SendSmsAsync(request, false); } - public override async Task>> ListSmssAsync(SmsStatus smsStatus) + public override async Task>> ListSmssAsync(SmsStatus smsStatus) { string command = $"AT+CMGL={(int)smsStatus}"; AtResponse response = await channel.SendMultilineCommand(command, null); - List smss = new List(); + List smss = new List(); if (response.Success) { if ((response.Intermediates.Count % 2) != 0) - return ModemResponse.HasResultError>(); + return ModemResponse.HasResultError>(); for (int i = 0; i < response.Intermediates.Count; i += 2) { @@ -71,7 +71,7 @@ public override async Task>> ListSmssAsync( // Sent when AT+CSDH=1 is set int length = int.Parse(match.Groups["length"].Value); - SmsBase sms = SmsDeliverDecoder.Decode(messageLine.ToByteArray()); + Sms sms = SmsDeliverDecoder.Decode(messageLine.ToByteArray()); smss.Add(sms.ToSmsWithIndex(index)); } } diff --git a/src/HeboTech.ATLib/PDU/SmsDecoder.cs b/src/HeboTech.ATLib/PDU/SmsDecoder.cs index f9701e7..bbc0cff 100644 --- a/src/HeboTech.ATLib/PDU/SmsDecoder.cs +++ b/src/HeboTech.ATLib/PDU/SmsDecoder.cs @@ -5,7 +5,7 @@ namespace HeboTech.ATLib.PDU { internal class SmsDecoder { - public static SmsBase Decode(ReadOnlySpan bytes, SmsStatus status, int timestampYearOffset = 2000) + public static Sms Decode(ReadOnlySpan bytes, SmsStatus status, int timestampYearOffset = 2000) { int offset = 0; From ca5e27a3a550f2efd4a1bf6467af896411f2616d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Thu, 2 May 2024 20:53:58 +0200 Subject: [PATCH 5/6] Simplify --- src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs index 1dc11d2..ede6e5e 100644 --- a/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs +++ b/src/HeboTech.ATLib/DTOs/SmsSubmitRequest.cs @@ -34,31 +34,10 @@ public SmsSubmitRequest( PhoneNumber phoneNumber, string message, CharacterSet codingScheme) - : this( - phoneNumber, - message, - codingScheme, - ValidityPeriod.NotPresent()) - { - } - - /// - /// Creates a data object for submitting an SMS in PDU format. - /// - /// The receiver phone number - /// The message to send - /// The coding scheme to use - /// The validity period to use - public SmsSubmitRequest( - PhoneNumber phoneNumber, - string message, - CharacterSet codingScheme, - ValidityPeriod validityPeriod) { PhoneNumber = phoneNumber; Message = message; CodingScheme = codingScheme; - ValidityPeriod = validityPeriod; } public PhoneNumber PhoneNumber { get; } From 8914abcf248e0d2afb93734f707dde77f33c5883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Bj=C3=B8rgo?= Date: Thu, 23 May 2024 21:40:09 +0200 Subject: [PATCH 6/6] Bump version --- src/HeboTech.ATLib/HeboTech.ATLib.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/HeboTech.ATLib/HeboTech.ATLib.csproj b/src/HeboTech.ATLib/HeboTech.ATLib.csproj index 4eb7f4e..6059117 100644 --- a/src/HeboTech.ATLib/HeboTech.ATLib.csproj +++ b/src/HeboTech.ATLib/HeboTech.ATLib.csproj @@ -4,10 +4,10 @@ netstandard2.1 HeboTech HeboTech ATLib - 7.1.0-beta2 - 7.1.0-beta2 - 7.1.0.0 - 7.1.0.0 + 8.0.0-alpha1 + 8.0.0-alpha1 + 8.0.0.0 + 8.0.0.0 HeboTech.ATLib AT command library that makes it easy to communicate with modems. AT command library that makes it easy to communicate with modems.