I'm currently working on a project where I use a serial port with a UWP app.
Sometimes I have the problem that I expect a certain amount of data but when I don't get that amount of data my serial port keeps reading until I cancel it manually.
When I initialize the serialDevice I set the serialDevice.ReadTimeout parameter but that doesn't do anything.
I also tried to bind a cancellationToken to the ReadTimeout parameter but that didn't work either. If I remember correctly it cancelled my method before it even finished...
Is there some way to implement a working timeout?
I use this as my base:
public class ReadWriteAdapter
{
public SemaphoreSlim Semaphore { get; }
private static readonly object LockObject = new object();
private static ReadWriteAdapter _instance;
public static ReadWriteAdapter Current
{
get
{
if (_instance == null)
{
lock (LockObject)
{
if (_instance == null)
{
_instance = new ReadWriteAdapter();
}
}
}
return _instance;
}
}
private ReadWriteAdapter()
{
Semaphore = new SemaphoreSlim(1, 1);
}
private SerialDevice _serialPort;
public CancellationTokenSource ReadCancellationTokenSource;
public CancellationTokenSource WriteCancellationTokenSource;
private object _readCancelLock = new object();
private object _writeCancelLock = new object();
private DataWriter _dataWriter;
private DataReader _dataReader;
public bool IsDeviceInitialized()
{
return _serialPort != null;
}
public async Task<string> Init()
{
try
{
if (_serialPort != null) return "port already configured!";
var aqs = SerialDevice.GetDeviceSelector("COM3");
var devices = await DeviceInformation.FindAllAsync(aqs, null);
if (!devices.Any()) return "no devices found!";
await OpenPort(devices[0].Id);
WriteCancellationTokenSource = new CancellationTokenSource();
ReadCancellationTokenSource = new CancellationTokenSource();
return "found port!";
}
catch (Exception ex)
{
return ex.Message;
}
}
private async Task OpenPort(string deviceId)
{
try
{
_serialPort = await SerialDevice.FromIdAsync(deviceId);
if (_serialPort != null)
{
_serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.BaudRate = 19200;
_serialPort.Parity = SerialParity.None;
_serialPort.StopBits = SerialStopBitCount.One;
_serialPort.DataBits = 8;
_serialPort.Handshake = SerialHandshake.None;
_dataWriter = new DataWriter(_serialPort.OutputStream);
_dataReader = new DataReader(_serialPort.InputStream);
}
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint readBufferLength)
{
Task<uint> loadAsyncTask;
var returnArray = new byte[readBufferLength];
lock (_readCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = _dataReader.LoadAsync(readBufferLength).AsTask(cancellationToken);
}
var bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
_dataReader.ReadBytes(returnArray);
}
return returnArray;
}
public async Task<uint> WriteCommand(string PairedCommand)
{
var commandArray = StringToByteArray(PairedCommand);
uint taskResult = 0;
try
{
if (_serialPort != null)
{
if (_dataWriter == null)
{
_dataWriter = new DataWriter(_serialPort.OutputStream);
taskResult = await WriteAsync(WriteCancellationTokenSource.Token, commandArray);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
_dataWriter.DetachStream();
_dataWriter.Dispose();
_dataWriter = null;
}
return taskResult;
}
public async Task<uint> WriteAsync(CancellationToken cancellationToken, byte[] data)
{
Task<uint> storeAsyncTask;
try
{
lock (_writeCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataWriter.WriteBytes(data);
storeAsyncTask = _dataWriter.StoreAsync().AsTask(cancellationToken);
}
}
catch (Exception ex)
{
throw ex;
}
return await storeAsyncTask;
}
public async Task<byte[]> Listen(uint bufferLength)
{
var listen = new byte[bufferLength];
try
{
if (_serialPort != null)
{
if (_dataReader == null)
{
_dataReader = new DataReader(_serialPort.InputStream);
}
listen = await ReadAsync(ReadCancellationTokenSource.Token, bufferLength);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (_dataReader != null)
{
_dataReader.DetachStream();
_dataReader.Dispose();
_dataReader = null;
}
}
return listen;
}
public byte[] StringToByteArray(string str)
{
var enc = new ASCIIEncoding();
return enc.GetBytes(str);
}
public void CancelReadTask()
{
lock (_readCancelLock)
{
if (ReadCancellationTokenSource == null) return;
if (ReadCancellationTokenSource.IsCancellationRequested) return;
ReadCancellationTokenSource.Cancel();
ReadCancellationTokenSource = new CancellationTokenSource();
}
}
private void CancelWriteTask()
{
lock (_writeCancelLock)
{
if (WriteCancellationTokenSource == null) return;
if (WriteCancellationTokenSource.IsCancellationRequested) return;
WriteCancellationTokenSource.Cancel();
WriteCancellationTokenSource = new CancellationTokenSource();
}
}
}
And I call it with a code similar to this (short version):
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
await ReadWriteAdapter.Current.WriteCommand("getData");
byte[] recievedArray = await ReadWriteAdapter.Current.Listen(100);
ReadWriteAdapter.Current.Semaphore.Release();
return recievedArray;
}
}
The simplest way to solve this issue is using the ReadTimeOut itself, but performing the reading independently of what was received.
Read ReadTimeOut Information in Microsoft WebSite
Definition: If ReadTimeout is set to TimeSpan.FromMilliseconds(ulong.MaxValue) (see TimeSpan), then a read request completes immediately with the bytes that have already been received, even if no bytes have been received.
In other words, this way it will immediately read whatever is in the serial buffer.
So you can implement it this way:
SerialDevice SerialDeviceComm;
Task.Run(async () => { SerialDeviceComm = await SerialDevice.FromIdAsync(_serialConnectionInfo[_indexPort].PortID); }).Wait();
SerialDeviceComm.BaudRate = _baudRate;
SerialDeviceComm.Parity = _parity;
SerialDeviceComm.StopBits = _stopBitCount;
SerialDeviceComm.DataBits = _dataBit;
SerialDeviceComm.Handshake = _serialHandshake;
// This will make that regardless of receiving bytes or not it will read and continue.
SerialDeviceComm.ReadTimeout = TimeSpan.FromMilliseconds(uint.MaxValue); // Secret is here
dataReader = new DataReader(SerialDeviceComm.InputStream)
{
InputStreamOptions = InputStreamOptions.Partial
};
string _tempData = "";
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
while (sw.ElapsedMilliseconds < timeOut)
{
uint receivedStringSize = 0;
Task.Delay(50).Wait(); // Time to take some bytes
dataReader.InputStreamOptions = InputStreamOptions.Partial;
Task.Run(async () => { receivedStringSize = await dataReader.LoadAsync(200); }).Wait();
_tempData += dataReader.ReadString(receivedStringSize);
}
You can see a example code in my git: GitHub with example
Related
Is there any way to convert the Task to a Task async or something similar?
I'm testing code, in short learning to perform different functions of Task type and learn to use its correct operation, however I have not been able to solve this "problem", so I want to ask people who have more knowledge about the Tasks.
Task<bool>.Factory.StartNew(() => {}); -> Task<bool>.Factory.StartNew(async () => {});
Here is a summary of what I want to do
private bool completed = false;
void Tester()
{
completed = false;
int iteration = 0;
var log = new Logger();
log.Log(Logger.Level.Debug, "Starting");
try
{
var theTask = Task<bool>.Factory.StartNew( () =>
{
while (!completed)
{
log.Log(Logger.Level.Debug, "Waiting",1);
iteration++;
Thread.Sleep(400);
if (iteration>20)
{
completed = true;
return true;
}
}
return false;
}).Result;
}
catch (Exception e)
{
log.Log(Logger.Level.Error, e.ToString());
}
finally
{
log.Log(Logger.Level.Info, "Completed");
}
}
You can do it. You can see this video to understand async/await pattern: https://www.youtube.com/watch?v=il9gl8MH17s.
Try this:
class Program
{
static async Task Main(string[] args)
{
bool result = await Tester();
System.Diagnostics.Debug.WriteLine($"result: {result}");
}
private static bool completed = false;
static async Task<bool> Tester()
{
completed = false;
int iteration = 0;
//var log = new Logger();
//log.Log(Logger.Level.Debug, "Starting");
try
{
await Task.Factory.StartNew(() =>
{
while (!completed)
{
//log.Log(Logger.Level.Debug, "Waiting", 1);
iteration++;
System.Diagnostics.Debug.WriteLine($"iteration {iteration}");
Task.Delay(2000);
if (iteration > 20)
{
completed = true;
}
}
});
}
catch (Exception e)
{
//log.Log(Logger.Level.Error, e.ToString());
}
finally
{
//log.Log(Logger.Level.Info, "Completed");
}
return completed;
}
}
I'm currently developing a WIn IoT app for the Raspberry Pi and I have a significant problem there...I could really need some help solving that...
The problem I'm having is that I have different commands I send over the serial port and every command recieves different responses.
After sending a few commands (can be just one or it can be 15...it's basically random -.-) my app just stops accepting the commands to send or completely crashes when I invoke one of the commands. It happens rather randomly and when I restart the app it's working again.
At least most of the time... Sometimes the first restart doesn't do anything and I have to restart again...
Since the app will probably be used in a bigger project I want to guarantee flawless execution. I don't want to have a customer (or the tool we use to communicate with the Pi) to restart the whole app. It should just abort it's non-working operation, log an error and then be back up running.
I think it may be a possibility that the buffer overflows for whatever reason... But I don't know of any way to clear the buffer from code in UWP. And I can't seem to find anything regarding that.
If you can help me I would be really happy. I'm trying to get this to work for almost a week but I can't find the problem...
My colleague rewrote some of my code last week. I originally passed every info I needed through tuples but he told me it's not good. So he wrote the class containing the info for me. But since then I run into a lot of problems. But I really don't know why it doesn't...
If you want to know more about my code feel free to ask. I really need this to work :/
Some of the code I'm using:
This is the block containing my data:
public class InfoBlock
{
public List<InfoBlockValue> Names { get; set; }
public byte[] ReceivedCrcSum { get; set; }
public byte[] CalculatedCrcSum { get; set; }
public bool IsChecksumEqual { get; set; } = false;
}
public class InfoBlockValue
{
public string InfoString { get; set; }
public InfoBlockValue(string info)
{
this.InfoString = info;
}
public override string ToString()
{
return InfoString;
}
}
This is the class for my Read and Write operations:
public class GetInfoBlock
{
public string SendCommand { get; set; } = "##getnames";
public async Task<uint> Write()
{
byte CarriageReturn = 0x0D;
byte[] WriteArray = StringToByteArray(SendCommand);
byte[] WriteArrayCR = new byte[WriteArray.Length + 1];
WriteArray.CopyTo(WriteArrayCR, 0);
WriteArrayCR[WriteArray.Length] = CarriageReturn;
return await ReadWriteAdapter.Current.WriteAsync(WriteArrayCR);
}
public async Task<InfoBlock> Read()
{
InfoBlock block = new InfoBlock();
byte[] ListenOut = await ReadWriteAdapter.Current.Listen(5802);
byte[] NameCrcSource = new byte[5800];
byte[] NameCrc16 = new byte[2];
Array.Copy(ListenOut, 0, NameCrcSource, 0, 5800);
block.Names = ConvertFromBytes(NameCrcSource);
block.ReceivedCrcSum = new byte[] { ListenOut[5801], ListenOut[5800] };
block.CalculatedCrcSum = Crc16Ccitt.ComputeChecksumBytes(NameCrcSource);
block.IsChecksumEqual = ValidateDataCRC.ValidateData(NameCrcSource, block.ReceivedCrcSum);
return block;
}
public List<InfoBlockValue> ConvertFromBytes(byte[] dataFromDrive)
{
List<InfoBlockValue> InfoValue = new List<InfoBlockValue>();
string[] allConvertedIntegers = new String[100];
int lastReadByte = 0;
int parameterIndex = 0;
int parameterByteIndex = 0;
for (parameterIndex = 0; parameterIndex < 99; parameterIndex++)
{
byte[] allBytesOfOneParameter = new byte[28];
Array.Copy(dataFromDrive, lastReadByte + 1, allBytesOfOneParameter, 0, 28);
InfoValue.Add(new InfoBlockValue(System.Text.Encoding.UTF8.GetString(allBytesOfOneParameter)));
parameterByteIndex = 0;
lastReadByte += 29;
}
return InfoValue;
}
public byte[] StringToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}
}
This is the code where I use the operations:
public class ReadInfo
{
private GetInfoBlock InfoBlock = null;
public async Task<Tuple<string, InfoBlock>> ReadNamesBlock()
{
if (InfoBlock == null)
{
InfoBlock = new GetInfoBlock();
}
await ReadWriteAdapter.semaphore.WaitAsync();
string returnString = string.Empty;
uint writeTuple = await InfoBlock.Write();
try
{
InfoBlock readTuple = await NamesBlock.Read();
bool validationResult = readTuple.IsChecksumEqual;
if (validationResult)
{
returnString += $"Checksum {BitConverter.ToString(readTuple.CalculatedCrcSum)} ReceivedCrcSum: {BitConverter.ToString(readTuple.ReceivedCrcSum)}";
//await ValidateDataCRC.SendAck();
}
else
{
returnString += "Checksum error";
await ValidateDataCRC.SendNack();
}
return new Tuple<string, InfoBlock>(returnString, readTuple);
}
catch (Exception ex)
{
string exception = $"Error while reading the parameternames from the device: {ex.Message}";
return new Tuple<string, InfoBlock>(exception, null);
}
finally
{
NamesBlock = null;
ReadWriteAdapter.semaphore.Release();
}
}
}
And the last one is my ReadWriteAdapter:
public class ReadWriteAdapter
{
public static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private static readonly Object lockObject = new object();
private static ReadWriteAdapter instance;
public static ReadWriteAdapter Current
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ReadWriteAdapter();
}
}
}
return instance;
}
}
private DataWriter dataWriter = null;
private DataReader dataReader = null;
private CancellationTokenSource ReadCancellationTokenSource;
private SerialDevice serialPort = null;
public bool IsDeviceInitialized()
{
return serialPort != null;
}
public async Task<string> Init()
{
try
{
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);
if (devices.Any())
{
if (devices[0].Id.Contains("FTDI"))
{
string deviceId = devices[0].Id;
await OpenPort(deviceId);
}
else
{
string deviceId = devices[1].Id;
await OpenPort(deviceId);
}
ReadCancellationTokenSource = new CancellationTokenSource();
dataWriter = new DataWriter(serialPort.OutputStream);
dataReader = new DataReader(serialPort.InputStream);
return "found port";
}
return "nodevices";
}
catch (Exception ex)
{
return ex.Message;
}
}
private async Task OpenPort(string deviceId)
{
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort != null)
{
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(500);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(500);
serialPort.BaudRate = 19200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
}
}
private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint ReadBufferLength)
{
Task<uint> loadAsyncTask;
byte[] returnArray = new byte[ReadBufferLength];
dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
uint bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
dataReader.ReadBytes(returnArray);
}
return returnArray;
}
public async Task<uint> WriteAsync(byte[] data)
{
if (serialPort == null)
{
throw new ArgumentNullException("device");
}
if (dataWriter == null)
{
throw new ArgumentNullException("device");
}
if (data.Length != 0)
{
dataWriter.WriteBytes(data);
// Launch an async task to complete the write operation
Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();
return await storeAsyncTask;
}
else
{
return 0;
}
}
public async Task<byte[]> Listen(uint BufferLength)
{
byte[] listen = new byte[BufferLength];
try
{
if (serialPort != null)
{
dataReader = new DataReader(serialPort.InputStream);
listen = await ReadAsync(ReadCancellationTokenSource.Token, BufferLength);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader = null;
}
}
return listen;
}
}
Found the answer myself...
I had to dispose the dataReader after I used it.
I guess the amount of streams I had on the reader just overflowed or whatever.
Changed this in ReadWriteAdapter:
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader = null;
}
}
To this:
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader.Dispose(); //Added this part
dataReader = null;
}
}
I want to make a Wifimanager class for UWP (Raspberry pi 3)
I looked at some tutorials and figured i was good to go.
So I came up with this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public WifiManager()
{
Initialize();
}
private async void Initialize()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
However when I try to get the Available Networks using the async function the adapter is null. When I remove the if statement around await adapter.ScanAsync(); I get an AccessViolation.
I dont have much experience with async tasks and such, but the tutorials i found did not gave me the explaination i needed to fix this.
The problem is that you are calling an async method from the constructor. Since you do no wait for the result and are probably calling GetAvailableNetWorksAsync directly after the constructor the adapter variable has not been set yet..
You need to take it out of the constructor like this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
Now your calling code probably looks like this:
var wifiManager = new WifiManager();
await wifiManager.GetAvailableNetWorksAsync();
change that to
var wifiManager = new WifiManager();
await wifiManager.InitializeAsync();
await wifiManager.GetAvailableNetWorksAsync();
Take away: do not call async methods in a constructor since you cannot await completion using the await keyword and using .Wait() or .Result might give other troubles . Applies for 99% of all situations.
Another approach could be something like:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
pivate async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter == null)
{
await InitializeAsync();
}
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
I have created a viewmodel that I connect to my current page where the user selects an image from his phonealbum.
When I pick a image it works perfectly fine and the picture appears on the screen just how I want but when I try to store it on my database I get a crash and I think it is (as I have been told) that it is due to the fact that my code does not get the imagedata.
Apparently there is a package called Xamarin.Plugin.Media and I have been fooling around a bit with that but with no luck.
This is my code:
My viewmodel:
public class CameraViewModel : ViewModel
{
private readonly TaskScheduler _scheduler = TaskScheduler.FromCurrentSynchronizationContext();
private IMediaPicker _Mediapicker;
private ImageSource _ImageSource;
private string _VideoInfo;
private Command _TakePictureCommand;
private Command _SelectPictureCommand;
private Command _SelectVideoCommand;
private string _Status;
public CameraViewModel()
{
Setup ();
}
public ImageSource ImageSource
{
get { return _ImageSource; }
set { SetProperty (ref _ImageSource, value); }
}
public string VideoInfo
{
get { return _VideoInfo; }
set { SetProperty (ref _VideoInfo, value); }
}
public Command TakePictureCommand
{
get {
return _TakePictureCommand ?? (_TakePictureCommand =
new Command (async () => await TakePicture (), () => true));
}
}
public Command SelectPictureCommand
{
get {
return _SelectPictureCommand ?? (_SelectPictureCommand =
new Command (async () => await SelectPicture (), () => true));
}
}
public Command SelectVideoCommand
{
get {
return _SelectVideoCommand ?? (_SelectVideoCommand =
new Command (async () => await SelectVideo (), () => true));
}
}
public string Status
{
get { return _Status; }
set { SetProperty (ref _Status, value); }
}
private void Setup()
{
if (_Mediapicker == null) {
var device = Resolver.Resolve<IDevice> ();
_Mediapicker = DependencyService.Get<IMediaPicker> () ?? device.MediaPicker;
}
}
public async Task<MediaFile> TakePicture()
{
Setup ();
ImageSource = null;
return await _Mediapicker.TakePhotoAsync (new CameraMediaStorageOptions {
DefaultCamera = CameraDevice.Front, MaxPixelDimension = 400
}).ContinueWith (t => {
if (t.IsFaulted)
{
Status = t.Exception.InnerException.ToString();
}
else if (t.IsCanceled)
{
Status = "Canceled";
}
else
{
var mediaFile = t.Result;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
return mediaFile;
}
return null;
}, _scheduler);
}
public async Task SelectPicture()
{
Setup ();
ImageSource = null;
try
{
var mediaFile = await _Mediapicker.SelectPhotoAsync(new CameraMediaStorageOptions
{
DefaultCamera = CameraDevice.Front,
MaxPixelDimension = 400
});
VideoInfo = mediaFile.Path;
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
}
catch (System.Exception ex)
{
Status = ex.Message;
}
}
public async Task SelectVideo()
{
Setup ();
VideoInfo = "Selecting video";
try
{
var mediaFile = await _Mediapicker.SelectVideoAsync(new VideoMediaStorageOptions());
VideoInfo = mediaFile != null
? string.Format("Your video size {0} MB", ConvertBytesToMegabytes(mediaFile.Source.Length))
: "No video was selected";
}
catch (System.Exception ex)
{
if (ex is TaskCanceledException) {
VideoInfo = "Selecting video cancelled";
} else {
VideoInfo = ex.Message;
}
}
}
private static double ConvertBytesToMegabytes(long bytes)
{
double rtn_value = (bytes / 1024f) / 1024f;
return rtn_value;
}
}
}
the Page that i am using:
CameraViewModel cameraOps = null;
public startPage ()
{
InitializeComponent ();
cameraOps = new CameraViewModel ();
}
private async void btnPickPicture_Clicked (object sender, EventArgs e)
{
await cameraOps.SelectPicture ();
imgPicked.Source = cameraOps.ImageSource;
}
So when I try to add my ImgPicked (Image in my XAML) in my database, it crashes. (Tell me if you would like to see the database-code as well)
And the crash says: Self referencing loop detected for property 'Member' with type 'System.Reflection.MonoMethod'. Path 'thePicture.Stream.Method.ReturnParameter'.
thePicture is where I try to store my image in my database.
The Media plugin returns a MediaFile object that has a couple of properties/methods you can use.
public string Path
is a property gives you the Path to the image file. You can use that to read the image data with File.ReadAllBytes() or some other method.
public Stream GetStream()
is a method that returns a stream with the image data. You can convert that stream to a byte array that will contain the image data.
You can use either of these in your ViewModel to get the byte[] data for the image, and later pass that data to your database calls.
You could modify your ViewModel to store the byte array for the image by doing something like this (untested)
private byte[] imageData;
public byte[] ImageData { get { return imageData; } }
private byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
public async Task SelectPicture()
{
Setup ();
ImageSource = null;
try
{
var mediaFile = await _Mediapicker.SelectPhotoAsync(new CameraMediaStorageOptions
{
DefaultCamera = CameraDevice.Front,
MaxPixelDimension = 400
});
VideoInfo = mediaFile.Path;
// store the image data in a local variable
imageData = ReadStream(mediaFile.GetStream());
ImageSource = ImageSource.FromStream(() => mediaFile.Source);
}
catch (System.Exception ex)
{
Status = ex.Message;
}
}
I'm trying to send an UDP Broadcast with Windows Phone 8.1. Therefore, I created a UDP Server (a console app) which runs on my laptop in the same Wifi Network as my WP8.1.
This is the test server:
static void Main(string[] args)
{
var client = new UdpClient(12345);
client.MulticastLoopback = false;
var isRunning = true;
var thread = new Thread(() =>
{
while (isRunning)
{
var endpoint = new IPEndPoint(IPAddress.Any, 0);
var data = client.Receive(ref endpoint);
Console.WriteLine("Received message from {0}:{1} with length={2}", endpoint.Address, endpoint.Port, data.Length);
client.Send(data, data.Length, endpoint);
}
});
thread.Start();
Console.ReadLine();
isRunning = false;
}
When I create another console app which sends some data as UDP Broadcast, I will get the same data as response from the server. So the server is working fine. With Windows Phone 8.1 I have to use DatagramSocket. This is my code:
var socket = new DatagramSocket();
socket.MessageReceived += HandleIncomingMessages;
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), "12345"))
{
await stream.WriteAsync(data.AsBuffer());
await stream.FlushAsync();
}
I also tried it with an additional await _socket.ConnectAsync(new HostName("255.255.255.255"), "12345"); after creating the socket object.
Unfortunately, I'll never get an answer.
This is my implementation for a UdpClient which supports broadcast.
public class UdpClient : IDisposable
{
private readonly DatagramSocket _socket;
private readonly BlockingCollection<Tuple<IpAddress, byte[]>> _incomingMessages;
private readonly IpAddress _ipAddress;
private readonly object _lock = new object();
private bool _isBound;
private bool _isBinding;
public UdpClient(IpAddress ipAddress)
{
_ipAddress = ipAddress;
_socket = new DatagramSocket();
_socket.Control.DontFragment = true;
_incomingMessages = new BlockingCollection<Tuple<IpAddress, byte[]>>();
_socket.MessageReceived += HandleIncomingMessages;
}
public async Task SendAsync(byte[] data)
{
try
{
await Connect();
using (var stream = await _socket.GetOutputStreamAsync(_ipAddress.Host, _ipAddress.ServiceName))
{
await stream.WriteAsync(data.AsBuffer(0, data.Length));
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
public bool TryGetIncomingMessage(out Tuple<IpAddress, byte[]> message)
{
return _incomingMessages.TryTake(out message, TimeSpan.FromMilliseconds(20));
}
public void Dispose()
{
_socket.Dispose();
}
private async Task Connect()
{
if (_isBound || _isBinding)
{
return;
}
lock (_lock)
{
if (_isBound || _isBinding)
{
return;
}
_isBinding = true;
}
var possibleConnectionProfiles = NetworkInformation.GetConnectionProfiles()
.Where(p => p.IsWlanConnectionProfile && p.GetNetworkConnectivityLevel() != NetworkConnectivityLevel.None)
.ToList();
var connectionProfile = possibleConnectionProfiles.FirstOrDefault();
if (connectionProfile != null)
{
await _socket.BindServiceNameAsync(_ipAddress.ServiceName, connectionProfile.NetworkAdapter);
}
_isBound = true;
_isBinding = false;
}
private void HandleIncomingMessages(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs e)
{
var address = e.RemoteAddress.ToString();
var port = int.Parse(e.RemotePort);
using (var reader = e.GetDataReader())
{
var data = reader.DetachBuffer().ToArray();
_incomingMessages.Add(Tuple.Create(new IpAddress(address, port), data));
}
}
}
public struct IpAddress
{
public static readonly IpAddress Broadcast = new IpAddress("255.255.255.255");
public readonly string Address;
public readonly int Port;
public readonly HostName Host;
public readonly string ServiceName;
public IpAddress(string address, int port)
{
Address = address;
Port = port;
Host = new HostName(address);
ServiceName = port.ToString(CultureInfo.InvariantCulture);
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", Address, Port);
}
public bool Equals(IpAddress other)
{
return string.Equals(Address, other.Address) && Port == other.Port;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
return obj is IpAddress && Equals((IpAddress)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((Address != null ? Address.GetHashCode() : 0) * 397) ^ Port;
}
}
}
I have read that it's not a good habit to broadcast messages to 255.255.255.255. Instead one should send to the local broadcast address like (i.e. 192.168.1.255)
For this I wrote this IpAddressProvider:
public static class IpProvider
{
private static readonly Lazy<string> _localAddress;
private static readonly Lazy<string> _broadcastAddress;
static IpProvider()
{
_localAddress = new Lazy<string>(GetLocalAddress);
_broadcastAddress = new Lazy<string>(GetBroadcastAddress);
}
public static string LocalAddress { get { return _localAddress.Value; } }
public static string BroadcastAddress { get { return _broadcastAddress.Value; } }
private static string GetLocalAddress()
{
var hostnames = NetworkInformation.GetHostNames();
foreach (var hn in hostnames)
{
//IanaInterfaceType == 71 => Wifi
//IanaInterfaceType == 6 => Ethernet (Emulator)
if (hn.IPInformation != null &&
(hn.IPInformation.NetworkAdapter.IanaInterfaceType == 71
|| hn.IPInformation.NetworkAdapter.IanaInterfaceType == 6))
{
return hn.DisplayName;
}
}
return IpAddress.Broadcast.Address;
}
private static string GetBroadcastAddress()
{
var parts = _localAddress.Value.Split(new[] { '.' }).Take(3);
return string.Join(".", parts) + ".255";
}
}
And now I use them together to send broadcast messages:
var gatewayAddress = new IpAddress(IpProvider.BroadcastAddress);
var udpClient = new UdpClient(gatewayAddress);
await udpClient.SendAsync(data);
Tuple<IpAddress, byte[]> message;
while (udpClient.TryGetIncomingMessage(out message))
{
if (message.Item1.Address == IpProvider.LocalAddress)
{
continue;
}
HandleIncomingPacket(message);
}