I need to send a message to Azure Iot Hub (https://azure.microsoft.com/it-it/services/iot-hub/) from an Universal App installed in my Raspberry. I've to use HTTP protocol because Raspberry doesn't supports AMQP.
I use the following code:
public sealed partial class MainPage : Page
{
private DispatcherTimer _timer = null;
private DeviceClient _deviceClient = null;
private const string _deviceConnectionString = "<myConnectionString>";
public MainPage()
{
InitializeComponent();
_deviceClient = DeviceClient.CreateFromConnectionString(_deviceConnectionString, TransportType.Http1);
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(5);
_timer.Tick += _timer_Tick;
_timer.Start();
}
private async void _timer_Tick(object sender, object e)
{
string msg = "{deviceId: 'myFirstDevice', timestamp: " + DateTime.Now.Ticks + " }";
Message eventMessage = new Message(Encoding.UTF8.GetBytes(msg));
await _deviceClient.SendEventAsync(eventMessage);
}
}
SendEventAsync gives me:
Exception thrown: 'Microsoft.Azure.Devices.Client.Exceptions.IotHubCommunicationException' in mscorlib.ni.dll
Message: {"An error occurred while sending the request."}
I've included in my project Microsoft.AspNet.WebApi.Client as documented here: https://github.com/Azure/azure-iot-sdks/issues/65 with no results.
"dependencies": {
"Microsoft.AspNet.WebApi.Client": "5.2.3",
"Microsoft.Azure.Devices.Client": "1.0.0-preview-007",
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
},
"frameworks": {
"uap10.0": {}
}
If I try the SAME code in a Console application it works as expected.
#danvy is right you need to generate a SAS token,here is a signature generator https://github.com/sandrinodimattia/RedDog/releases
There could be multiple ways to send events, you are using http, check out this sample
// Generate a SAS key with the Signature Generator.:
var sas = "SharedAccessSignature sr=https%3a%2f%2freddogeventhub.servicebus.windows.net%2ftemperature%2fpublishers%2flivingroom%2fmessages&sig=I7n%2bqlIExBRs23V4mcYYfYVYhc6adOlMAeTY9VM9kNg%3d&se=1405562228&skn=SenderDevice";
// Namespace info.
var serviceNamespace = "myeventhub";
var hubName = "temperature";
var deviceName = "livingroom";
// Create client.
var httpClient = new HttpClient();
httpClient.BaseAddress =
new Uri(String.Format("https://{0}.servicebus.windows.net/", serviceNamespace));
httpClient.DefaultRequestHeaders
.TryAddWithoutValidation("Authorization", sas);
Console.WriteLine("Starting device: {0}", deviceName);
// Keep sending.
while (true)
{
var eventData = new
{
Temperature = new Random().Next(20, 50)
};
var postResult = httpClient.PostAsJsonAsync(
String.Format("{0}/publishers/{1}/messages", hubName, deviceName), eventData).Result;
Console.WriteLine("Sent temperature using HttpClient: {0}",
eventData.Temperature);
Console.WriteLine(" > Response: {0}",
postResult.StatusCode);
Console.WriteLine(" > Response Content: {0}",
postResult.Content.ReadAsStringAsync().Result);
Thread.Sleep(new Random().Next(1000, 5000));
}
Check out this article for more details http://fabriccontroller.net/iot-with-azure-service-bus-event-hubs-authenticating-and-sending-from-any-type-of-device-net-and-js-samples/
try to set a connection string to your device (_deviceConnectionString) using this tutorial
https://github.com/Azure/azure-iot-sdks/blob/master/tools/DeviceExplorer/doc/how_to_use_device_explorer.md
You can do it by hand using the information you get from the IoT Hub directly or from the dashboard created by the IoT Suite wizard. It will look like this
_deviceConnectionString = "HostName=YourIoTHubName.azure-devices.net;DeviceId=YourDeviceId;SharedAccessKey=YourDeviceSharedAccessKey";
Did you generate the DeviceId and device key correctly using CreateDeviceIdentity tool?
Here is the guide: https://blogs.windows.com/buildingapps/2015/12/09/windows-iot-core-and-azure-iot-hub-putting-the-i-in-iot/
After hours of searching I found an hardware problem. My Raspberry was trying to use the non-configured Wi-Fi dongle to send the request while for all other requests used the network cable. Removing the dongle did the trick.
Related
I am learning Azure Events Hub. A simple application i have downloaded from this link https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-dotnet-standard-getstarted-send . But when i try to send message, its giving me this error:
10/23/2018 11:11:13 PM > Exception: Put token failed. status-code:
404, status-description: The messaging entity
'sb://demo.servicebus.windows.net/myTeam' could not
be found. TrackingId:[My Tracking ID],
SystemTracker:iot-bd-madness.servicebus.windows.net:IoT-BD-Madness,
Timestamp:10/23/2018 5:11:18 PM.
In Azure Event Hub Dashboard all incoming requests (sending from console app) are visible with chart. but those are all request actually failed when i tried in console application
N.B:the given connectionstring is not real
public class Program
{
private static EventHubClient eventHubClient;
private const string EventHubConnectionString = "Endpoint=sb://iot-bd-madness.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
private const string EventHubName = "Iot-Bd-Madness";
public static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
private static async Task MainAsync(string[] args)
{
// Creates an EventHubsConnectionStringBuilder object from a the connection string, and sets the EntityPath.
// Typically the connection string should have the Entity Path in it, but for the sake of this simple scenario
// we are using the connection string from the namespace.
var connectionStringBuilder = new EventHubsConnectionStringBuilder(EventHubConnectionString)
{
EntityPath = EventHubName
};
eventHubClient = EventHubClient.CreateFromConnectionString(connectionStringBuilder.ToString());
await SendMessagesToEventHub(100);
await eventHubClient.CloseAsync();
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
// Creates an Event Hub client and sends 100 messages to the event hub.
private static async Task SendMessagesToEventHub(int numMessagesToSend)
{
for (var i = 0; i < numMessagesToSend; i++)
{
try
{
var message = $"Message {i}";
Console.WriteLine($"Sending message: {message}");
await eventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes(message)));
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} > Exception: {exception.Message}");
}
await Task.Delay(10);
}
Console.WriteLine($"{numMessagesToSend} messages sent.");
}
}
}
I ran into same problem. My EventHubName = "myeventhubname" was wrong. I passed the Event Hubs Namespace value - rounded in red. It gave error.
I changed it to value under Event Hub page left column -> click Entities -> Event Hubs
I used the name that was shown in the table rounded in green.
i have followed this link To write custom modules. In this tutorial A module named tempSensor sending data to another Module CSharpModule. As far as tutorial is concerned, I successfully implemented it.
What i want to do is: Receiving telemetry data from IoTDevice to IoTEdge Device.
Architecture: Azure IoTHub connected with IoT devices and an IoTEdge device
What i have tried: I tried to send telemetry data from a simulated device connected to ioTEdge with connectionString.
The code to send data is here
//DeviceClient connected to IoTEdge
s_deviceClient = DeviceClient.CreateFromConnectionString(s_connectionString);
private static async void SendDeviceToCloudMessagesAsync()
{
// Initial telemetry values
double minTemperature = 20;
double minHumidity = 60;
Random rand = new Random();
while (true)
{
double currentTemperature = minTemperature + rand.NextDouble() * 15;
double currentHumidity = minHumidity + rand.NextDouble() * 20;
// Create JSON message
var telemetryDataPoint = new
{
temperature = currentTemperature,
humidity = currentHumidity
};
var messageString = JsonConvert.SerializeObject(telemetryDataPoint);
var message = new Message(Encoding.ASCII.GetBytes(messageString));
// Add a custom application property to the message.
message.Properties.Add("temperatureAlert", (currentTemperature > 30) ? "true" : "false");
// Send the tlemetry message to endpoint output1
await s_deviceClient.SendEventAsync("ouput1",message);
//await s_deviceClient.SendEventAsync(message);
Console.WriteLine("{0} > Sending message: {1}", DateTime.Now, messageString);
await Task.Delay(s_telemetryInterval * 10000);
}
}
IoTEdge custom module code's receiving end is here...
static async Task Init()
{
AmqpTransportSettings amqpSetting = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only);
ITransportSettings[] settings = { amqpSetting };
// Open a connection to the Edge runtime
ModuleClient ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
await ioTHubModuleClient.OpenAsync();
Console.WriteLine("IoT Hub module client initialized.");
// Register a callback for messages that are received by the module.
// await ioTHubModuleClient.SetImputMessageHandlerAsync("input1", PipeMessage, iotHubModuleClient);
// Read the TemperatureThreshold value from the module twin's desired properties
var moduleTwin = await ioTHubModuleClient.GetTwinAsync();
var moduleTwinCollection = moduleTwin.Properties.Desired;
try {
temperatureThreshold = moduleTwinCollection["iothub-connection-device-id"];
} catch(ArgumentOutOfRangeException e) {
Console.WriteLine($"Property TemperatureThreshold not exist: {e.Message}");
}
// Attach a callback for updates to the module twin's desired properties.
await ioTHubModuleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertiesUpdate, null);
// Register a callback for messages that are received by the module
await ioTHubModuleClient.SetInputMessageHandlerAsync("input1", PipeMessage, ioTHubModuleClient);
}
The route information from deployment.template.json file of custom module is as following.
"routes": {
"aggregationModuleToIoTHub": "FROM /messages/modules/aggregationModule/outputs/* INTO $upstream"
}
But the problem is the callback PipeMessage never called. My understanding is no message is coming at IoTEdge endpoint messages/input1.
To make sure I understand your question, you are trying to send code from an IoT Device outside of IoT Edge into IoT Edge, and then route that data through a module in IoT Edge up to IoT Hub? is that correct? If that's the case, we call the IoT Device a downstream or "leaf" device. IoT Edge needs to be set up as a 'transparent gateway' like this documentation. Once you do that, you need to add ;GatewayHostName= to the end of your connection string where is the name you used as the 'hostname' parameter in your config.yaml file.
I'm using the IBM websphere XMS API to connect and send messages to the mainframe. However, every message sent is sent through a new local port. Is there a way to set this to a fixed port?
A new port is created locally when the following line is hit:
var connContext = new XMSConnectionContext(connFactory.CreateConnection(), sendQ, replyQ, mqProfile, DateTime.Now.AddSeconds(qm.MqConfiguration.ConnectionPoolExpiryTime));
The code I'm using is
public IMQMessage Message { get; set; }
public void Initialise(IMQMessage message, QueueSet queueSet, QueueManager queueManager)
{
Message = message;
if (_connContext.ContainsKey(message.MessageId)) return;
_connContext.TryAdd(message.MessageId, ConnectQueueSet(queueSet, queueManager));
_connContext[message.MessageId].Connection.Start();
}
private XMSConnectionContext ConnectQueueSet(MQQueueSet queueSet, QueueManager qm)
{
var mqProfile = GetProfile(queueSet);
var xmsFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
var connFactory = xmsFactory.CreateConnectionFactory();
connFactory.SetStringProperty(XMSC.WMQ_HOST_NAME, mqProfile.ServerName);
connFactory.SetIntProperty(XMSC.WMQ_PORT, mqProfile.Port);
connFactory.SetStringProperty(XMSC.WMQ_CHANNEL, mqProfile.ChannelName);
connFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, mqProfile.QueueManagerName);
connFactory.SetIntProperty(XMSC.WMQ_FAIL_IF_QUIESCE, 1);
connFactory.SetIntProperty(XMSC.WMQ_SHARE_CONV_ALLOWED, XMSC.WMQ_SHARE_CONV_ALLOWED_YES);
connFactory.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, XMSC.WMQ_CM_CLIENT);
We've tried
connFactory.SetStringProperty(XMSC.XMSC_WMQ_LOCAL_ADDRESS,"(45000,45010)");
We've also tried
connFactory.SetStringProperty(XMSC.XMSC_WMQ_LOCAL_ADDRESS,"localhost(45000,45010)");
We've also tried
connFactory.SetStringProperty(XMSC.XMSC_WMQ_LOCAL_ADDRESS,"192.168.12.156(45000,45010)");
End of tests and the rest below is as it was.
IDestination sendQ = xmsFactory.CreateQueue(string.Format("queue://{0}/{1}?targetClient=1", mqProfile.QueueManagerName, mqProfile.RequestQueue));
IDestination replyQ = xmsFactory.CreateQueue(string.Format("queue://{0}/{1}?targetClient=1", mqProfile.QueueManagerName, mqProfile.ReplyQueue));
var connContext = new XMSConnectionContext(connFactory.CreateConnection(), sendQ, replyQ, mqProfile, DateTime.Now.AddSeconds(qm.MqConfiguration.ConnectionPoolExpiryTime));
QueueManager.Log.DebugFormat("XMSConnectionContext-Instantiated: ProfileName={0} SendQ={1}, ReplyQ={2}, ConnectionMetaData={3}", connContext.ProfileName, connContext.SendQ, connContext.ReplyQ, connContext.Connection);
return connContext;
}
public void Close()
{
if (_connContext != null)
{
_connContext[Message.MessageId].Connection.Stop();
}
}
Any help would be appreciated. Thanks.
XMS .NET has a connection factory property, XMSC_WMQ_LOCAL_ADDRESS that lets you specify a local port to be used while connecting to a queue manager. More details here
On initialize of my router application I call the following code. It binds fine, receives messages fine but refuses to work for the On_ReceiveXXX methods unless it's a direct response. I want to know why
public void Initialize(string frontEndAddress, string backEndAddress)
{
_poller = new Poller();
_timeAllowedBetweenPings = TimeSpan.FromMinutes(1);
_lastPingResponse = DateTime.Now;
using (var ctx = NetMQContext.Create())
{
_frontEnd = ctx.CreateRouterSocket();
_backEnd = ctx.CreateRouterSocket();
_frontEnd.Bind(frontEndAddress);
Console.WriteLine(string.Format("[Router]: Connected to {0}", frontEndAddress));
_backEnd.Bind(backEndAddress);
Console.WriteLine(string.Format("[Router]: Connected to {0}", backEndAddress));
_frontEnd.ReceiveReady += On_ReceiveFrontEnd;
_backEnd.ReceiveReady += On_ReceiveBackEnd;
_poller.AddSocket(_frontEnd);
_poller.AddSocket(_backEnd);
var timer = new NetMQTimer(TimeSpan.FromSeconds(1));
timer.Elapsed += On_Ping;
_poller.AddTimer(timer);
_poller.PollTillCancelled();
}
}
This fails to call the dealer ReceiveReady event:
private void On_ReceiveFrontEnd(object sender, NetMQSocketEventArgs e)
{
_lastPingResponse = DateTime.Now;
var frontEndMsg = e.Socket.ReceiveMultipartBytes();
var streamData = frontEndMsg.Last();
ApplicationMessage msg = PackageHelper.DeserializeOutgoing(streamData);
Console.WriteLine(string.Format("Command received: {0}", msg.CO));
_backEnd.SendMultipartBytes(frontEndMsg);
}
BUT if I change the line
_backEnd.SendMultipartBytes(frontEndMsg);
to
_frontEnd.SendMultipartBytes(frontEndMsg);
It suddenly works... so messages coming from my front end application can only be responded to, not passed on to the back end application. The same is true the other way round, for the back end messages.
When working with router the first frame is the routing id and it specific to the socket. So you can't pass the entire message from router to router. Change the backend to dealer and it will work, or prefix the message with routing id of the backend socket.
I'm new to the Azure Service Bus and am working on a proof of concept using Service Bus Queues, WebJobs, v2.5 of the Azure SDK and Visual Studio 2013
Enqueuing and de-queuing messages from the bus is pretty straightforward, but in order to implement a request-response pattern it looks like I need to use sessions, and that's where the wheels have come off.
Here's the proof-of-concept code from the WebJobs project. It requires that you create two service bus queues: test-request and test-response. The response queue must have Enable Sessions = true
class Program
{
private static string _azureServiceBusConnection;
static void Main()
{
_azureServiceBusConnection = ConfigurationManager.ConnectionStrings["AzureWebJobsServiceBus"].ConnectionString;
var host = new JobHost();
Task.Factory.StartNew(() => Run());
try
{
host.RunAndBlock();
}
catch (Exception ex)
{
Console.WriteLine("RunAndBlock() Unexpected {0}: {1}", ex.GetType().FullName, ex.Message);
throw;
}
}
private static async Task Run()
{
await Task.Delay(1000);
var request = QueueClient.CreateFromConnectionString(_azureServiceBusConnection, "test-request");
var response = QueueClient.CreateFromConnectionString(_azureServiceBusConnection, "test-response");
var sessionid = Guid.NewGuid().ToString();
MessageSession receiver;
try
{
receiver = response.AcceptMessageSession(sessionid);
}
catch (Exception ex )
{
Console.WriteLine("AcceptMessageSession() Unexpected {0}: {1}", ex.GetType().FullName, ex.Message);
throw;
}
var payload = new RequestModel {ID = Guid.NewGuid(), Delay = 1};
var message = new BrokeredMessage(payload) {ReplyToSessionId = sessionid};
try
{
request.Send(message);
}
catch (Exception ex)
{
Console.WriteLine("Send() Unexpected {0}: {1}", ex.GetType().FullName, ex.Message);
throw;
}
var receivedMessage = receiver.Receive(TimeSpan.FromSeconds(5));
if (receivedMessage != null)
{
// Request processed within the timeout period
var responseBody = receivedMessage.GetBody<RequestModel>();
Console.WriteLine("Inline response to {0}", responseBody.ID );
receivedMessage.Complete();
}
else
{
// Request processing timed out - should be handled by LocalResponseQueue WebJob (see below)
Console.WriteLine("ERROR: Response timed out.");
}
}
}
public class RequestModel
{
public Guid ID { get; set; }
public int Delay { get; set; }
}
public class RemoteSystemRequestQueue
{
// Simulates the processing of the request on a remote system
public static void ProcessQueueMessage([ServiceBusTrigger("test-request")] BrokeredMessage request, [ServiceBus("test-response")] out BrokeredMessage response)
{
// Wait for the prescribed delay, then bounce the request payload back via the response queue
var requestBody = request.GetBody<RequestModel>();
Console.WriteLine("Recieved Request {0}, delay={1}", requestBody.ID, requestBody.Delay);
Task.Delay(requestBody.Delay * 1000).Wait();
response = new BrokeredMessage(requestBody) {SessionId = request.ReplyToSessionId};
request.Complete();
Console.WriteLine("Completed Request {0}, delay={1}", requestBody.ID, requestBody.Delay);
}
}
public class LocalResponseQueue
{
// Should be called ONLY when the processing took longer than the timeout
public static void ProcessQueueMessage([ServiceBusTrigger("test-response")] BrokeredMessage message, TextWriter logger)
{
var msgBody = message.GetBody<RequestModel>();
Console.WriteLine("ResponseFactory Recieved Reply {0}", msgBody.ID);
}
}
When Enable Sessions = true on the test-response queue, the call to host.RunAndBlock() throws a System.InvalidOperationException with the message
It is not possible for an entity that requires sessions to create a non-sessionful message receiver
The output looks like this:
Found the following functions:
ServiceBusPoc.RemoteSystemRequestQueue.ProcessQueueMessage
ServiceBusPoc.LocalResponseQueue.ProcessQueueMessage
Executing: 'RemoteSystemRequestQueue.ProcessQueueMessage' because New service bus message detected on 'test-request'.
Recieved Request 4f000f8f-dd69-4909-9ec4-020fec12366c, delay=1
RunAndBlock() Unexpected System.InvalidOperationException: It is not possible for an entity that requires sessions to create a non-sessionful message receiver.
TrackingId:7836ac90-6920-4e6c-b7f1-cf648e2a17e5_G38_B10,TimeStamp:10/6/2015 12:37:05 PM
Note that the exception was thrown BEFORE the RemoteSystemRequestQueue object could complete processing the queued request
I presume from this that this means that WebJobs can't handle sessions (at least in the manner in which I'm using them)
Can anyone shed any light on this, or am I going to have to give up on WebJobs?
What version of the WebJobs SDK are you using? In our current v1.1.0 release (still prerelease) we've started opening up more options for ServiceBus configuration. See:
ServiceBusConfiguration
Custom MessagingProvider
You can basically now control more of the messaging details that were previously buried in the SDK internals. However, regarding full Session support, we do have this open issue that might be closer to your scenario. If so, and if you can't get things to work with the above, please add your scenario details to that issue. Thanks.