I've successfully got Akavache working for a Windows desktop, .NET 4.5 WPF project, but when I try to build it for the Xamarin (iOS and Android) targets, the BlobCache singleton is not properly initialized. BlobCache.Secure is null. (I've tried both the SQLite and 'vanilla' builds)
I'll be honest, I find the examples/documentation for Akavache a bit thin. I'm not a user of the Reactive stuff, I find much of Paul's code very opaque.
I'm just trying to do some very simple, secure caching of app state for a cross-platform app.
// where we store the user's application state
BlobCache.ApplicationName = "myApp";
BlobCache.EnsureInitialized();
public AppState State
{
get
{
return _appState;
}
set
{
_appState = value;
}
}
public void Load()
{
try
{
State = BlobCache.Secure.GetObjectAsync<AppState>.FirstOrDefault();
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
State = new AppState();
}
}
public void Save()
{
try
{
BlobCache.Secure.InsertObject("AppState", State);
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
So, there are some dumb tricks you have to do right now on Xamarin, that I've only very recently found out. I'm going to add these to the docs (or in the Android case, just fix the bug)
Xamarin.iOS
On iOS, Type.GetType() won't load assemblies, which isn't the same as any other platform. So, you have to run this silly goose code in your AppDelegate:
var r = new ModernDependencyResolver();
(new ReactiveUI.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Cocoa.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Mobile.Registrations()).Register((f,t) => r.Register(f, t));
RxApp.DependencyResolver = r;
(new Akavache.Registrations()).Register(r.Register);
(new Akavache.Mobile.Registrations()).Register(r.Register);
(new Akavache.Sqlite3.Registrations()).Register(r.Register);
Normally, this code runs AutoMagically™.
Xamarin.Android
Registration works fine on Xamarin.Android, but because of what I suspect is a bug in Akavache, you may have to register for AutoSuspend (even if you don't use it).
In all your activities, declare AutoSuspendActivityHelper autoSuspendHelper;
In the constructor, add:
autoSuspendHelper = new AutoSuspendActivityHelper(this);
autoSuspendHelper.OnCreate(bundle);
Override OnPause, OnResume, and OnSaveInstanceState and call the appropriate autoSuspendHelper method i.e:
autoSuspendHelper.OnPause();
More trouble?
Please let me know, either by Emailing me at paul#github.com or filing issues at github/akavache. I've shipped a production application with Akavache that runs on both iOS and Android, and it definitely works, but I realize it might be a bit Tricky™ to get stuff to work.
Related
Marshal.GetExceptionCode() has been obsoleted, and the message does not suggest a way forward.
at the moment I use this in the following opensource project:
https://github.com/dbones-labs/auditable/blob/master/src/Auditable/AuditableContext.cs#L143
the idea is, if the client code/app within a using block throws an exception it should not write a log.
However, the project code only has a using statement to figure out if it should react or not.
what is the alternative to this? (without changeing the API for the calling code)
example use of the API
https://dbones-labs.github.io/auditable/quick-examples/aspnet-example.html#3-add-some-auditable-logs
From a quick test it seems you can use Marshal.GetExceptionPointers():
public class MyDisposable : IDisposable
{
public void Dispose()
{
var ptr = Marshal.GetExceptionPointers();
Console.WriteLine(ptr);
}
}
try
{
using (new MyDisposable())
{
}
using (new MyDisposable())
{
throw new Exception();
}
}
catch (Exception)
{
}
using (new MyDisposable())
{
}
It is non-zero if there is an Exception "in the air".
Note that Marshal.GetExceptionPointers() was always present in .NET Framework but was reintroduced in .NET Core only >= 3.0. So for .NET Core 1.0-2.2 you'll need to use Marshal.GetExceptionCode()`.
I want to open a PWM pin to my buzzer. But If I try to call the pwmController.OpenPin(6) method, the app crashes with an System.Runtime.InteropServices.SEHException.
I had already double checked the sample sources like the ms-iot-samples. But I cannot see what my problems are.
An idea was that some permissions are missing, but if I try to add for exmaple <iot:Capability Name="lowLevelDevices" />, I cannot longer build the application.
Source
private PwmPin buzzerPin;
private PwmController pwmController;
public RainbowHAT()
{
// ... do something else
InitAsync();
}
private async void InitAsync()
{
Logger.Log(this, "Init");
// Setup PWM controller.
if (LightningProvider.IsLightningEnabled)
{
LowLevelDevicesController.DefaultProvider = LightningProvider.GetAggregateProvider();
}
var pwmControllers = await PwmController.GetControllersAsync(LightningPwmProvider.GetPwmProvider());
if (pwmControllers == null || pwmControllers.Count < 2)
{
throw new OperationCanceledException("Operation canceled due missing GPIO controller");
}
pwmController = pwmControllers[1];
pwmController.SetDesiredFrequency(50);
// Setup buzzer
buzzerPin = pwmController.OpenPin(13); <-- CRASH
buzzerPin.SetActiveDutyCyclePercentage(0.05);
buzzerPin.Start();
}
I also tried the following tip to reduce the min required Windows version, but this does not help, too.
PWM Controller needs Lightning support. So you need to set the controller driver as Direct Memory Mapped Driver. Here is a sample about PWM on Raspberry Pi.
You also need to modify the code as following:
private async void InitAsync()
{
Logger.Log(this, "Init");
// Setup PWM controller.
if (LightningProvider.IsLightningEnabled)
{
var pwmControllers = await PwmController.GetControllersAsync(LightningPwmProvider.GetPwmProvider());
if (pwmControllers == null || pwmControllers.Count < 2)
{
throw new OperationCanceledException("Operation canceled due missing GPIO controller");
}
pwmController = pwmControllers[1];
pwmController.SetDesiredFrequency(50);
// Setup buzzer
buzzerPin = pwmController.OpenPin(13);
buzzerPin.SetActiveDutyCyclePercentage(0.05);
buzzerPin.Start();
}
}
How can I set the display to stereoscopic programmatically in Unity for an app deployed to an Android device?
I want a UI menu where the user can toggle between "VR mode" and normal mode. I do not want VR mode by default as it should be an option at run-time. I know there is a setting for "Virtual Reality Supported" in the build settings, but again, I do not want this enabled by default.
Include using UnityEngine.XR; at the top.
Call XRSettings.LoadDeviceByName("") with empty string followed by XRSettings.enabled = false; to disable VR in the start function to disable VR.
When you want to enable it later on, call XRSettings.LoadDeviceByName("daydream") with the VR name followed by XRSettings.enabled = true;.
You should wait for a frame between each function call. That requires this to be done a corutine function.
Also, On some VR devices, you must go to Edit->Project Settings->Player and make sure that Virtual Reality Supported check-box is checked(true) before this will work. Then you can disable it in the Start function and enable it whenever you want.
EDIT:
This is known to work on some VR devices and not all VR devices. Although, it should work on Daydream VR. Complete code sample:
IEnumerator LoadDevice(string newDevice, bool enable)
{
XRSettings.LoadDeviceByName(newDevice);
yield return null;
XRSettings.enabled = enable;
}
void EnableVR()
{
StartCoroutine(LoadDevice("daydream", true));
}
void DisableVR()
{
StartCoroutine(LoadDevice("", false));
}
Call EnableVR() to enable vr and DisableVR() to disable it. If you are using anything other than daydream, pass the name of that VR device to the LoadDevice function in the EnableVR() function.
For newer builds of Unity (e.g. 2019.4.0f1) you can use the XR Plugin Management package.
To enable call:
XRGeneralSettings.Instance.Manager.InitializeLoader();
To disable call:
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
I'm using Unity 2021 but this probably works in earlier versions, I'm also using XR Plug-in Management.
Start:
XRGeneralSettings.Instance.Manager.StartSubsystems();
Stop:
XRGeneralSettings.Instance.Manager.StopSubsystems();
Full documentation at:
https://docs.unity3d.com/Packages/com.unity.xr.management#4.0/manual/EndUser.html
2020.3.14f1
Doesn't work for me, I get this error when running my Android app.
Call to DeinitializeLoader without an initialized manager.Please make
sure wait for initialization to complete before calling this API.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void TryToDeinitializeOculusLoader()
{
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
}
More context.
I try to unload the Oculus loader, before he manages to load the plugin.
I have an Android app, and the Oculus loader calls Application.Quit because the device is not an Oculus headset.
Waiting for XRGeneralSettings.Instance.Manager.isInitializationComplete takes too long.
Tried all RuntimeInitializeLoadType annotations.
OculusLoader.cs
#elif (UNITY_ANDROID && !UNITY_EDITOR)
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void RuntimeLoadOVRPlugin()
{
var supported = IsDeviceSupported();
if (supported == DeviceSupportedResult.ExitApplication)
{
Debug.LogError("\n\nExiting application:\n\nThis .apk was built with the Oculus XR Plugin loader enabled, but is attempting to run on a non-Oculus device.\nTo build for general Android devices, please disable the Oculus XR Plugin before building the Android player.\n\n\n");
Application.Quit();
}
if (supported != DeviceSupportedResult.Supported)
return;
try
{
if (!NativeMethods.LoadOVRPlugin(""))
Debug.LogError("Failed to load libOVRPlugin.so");
}
catch
{
// handle Android standalone build with Oculus XR Plugin installed but disabled in loader list.
}
}
#endif
SOLUTION
Made my build class extend IPreprocessBuildWithReport
public void OnPreprocessBuild(BuildReport report)
{
DisableXRLoaders(report);
}
///https://docs.unity3d.com/Packages/com.unity.xr.management#3.2/manual/EndUser.html
/// Do this as a setup step before you start a build, because the first thing that XR Plug-in Manager does at build time
/// is to serialize the loader list to the build target.
void DisableXRLoaders(BuildReport report)
{
XRGeneralSettingsPerBuildTarget buildTargetSettings;
EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings);
if (buildTargetSettings == null)
{
return;
}
XRGeneralSettings settings = buildTargetSettings.SettingsForBuildTarget(report.summary.platformGroup);
if (settings == null)
{
return;
}
XRManagerSettings loaderManager = settings.AssignedSettings;
if (loaderManager == null)
{
return;
}
var loaders = loaderManager.activeLoaders;
// If there are no loaders present in the current manager instance, then the settings will not be included in the current build.
if (loaders.Count == 0)
{
return;
}
var loadersForRemoval = new List<XRLoader>();
loadersForRemoval.AddRange(loaders);
foreach (var loader in loadersForRemoval)
{
loaderManager.TryRemoveLoader(loader);
}
}
public void Awake() {
StartCoroutine(SwitchToVR(()=>{
Debug.Log("Switched to VR Mode");
}));
//For disable VR Mode
XRSettings.enabled = false;
}
IEnumerator SwitchToVR(Action callback) {
// Device names are lowercase, as returned by `XRSettings.supportedDevices`.
// Google original, makes you specify
// string desiredDevice = "daydream"; // Or "cardboard".
// XRSettings.LoadDeviceByName(desiredDevice);
// this is slightly better;
string[] Devices = new string[] { "daydream", "cardboard" };
XRSettings.LoadDeviceByName(Devices);
// Must wait one frame after calling `XRSettings.LoadDeviceByName()`.
yield return null;
// Now it's ok to enable VR mode.
XRSettings.enabled = true;
callback.Invoke();
}
I am new here and I hope that i will find a solution for my problem. The background of the problem is as follows:
I am trying to build an expert system that constitute a C# front-end which is interacting with Swi-prolog.
I have downloaded SwiPlCs.dll (A CSharp class library to connect .NET languages with Swi-Prolog)
And added a reference to it in a Visual Studio project(Win form app) that I have created to test if I can query prolog from c# (I followed the example used in the documentation found here).
It worked fine.
Then, in a more complicated scenario, I have built a WCF service that will act as an intermediary layer between Swi-Prolog and C# client application (it consumes the service).
The service is hosted in IIS 7.0.
For the sake of simplicity, lets say my service contains three methods.
The first method initializes the prolog engine, consults prolog source file then queries the file.
The second method performs another query.
The third method calls PlCleanup().
Method#1:
public void LaunchAssessment()
{
Dictionary<string, string> questions = new Dictionary<string, string>();
#region : Querying prolog using SwiPlCs
try
{
if (!PlEngine.IsInitialized)
{
String[] param = { "-q" };
PlEngine.Initialize(param);
PlQuery.PlCall("consult('D:/My FYP Work/initialAssessment')");
using (var q = new PlQuery("go(X, Y)"))
{
foreach (PlQueryVariables v in q.SolutionVariables)
{
questions.Add("name", v["X"].ToString());
questions.Add("age", v["Y"].ToString());
}
}
}
}
catch (SbsSW.SwiPlCs.Exceptions.PlException exp)
{
throw new FaultException<PrologFault>(new PrologFault(exp.Source), exp.MessagePl);
}
#endregion
Callback.PoseQuestion(questions, ResponseType.None);
}
Method#2:
public void DetermineAgeGroup(int age)
{
//Determine age group
string age_group = string.Empty;
try
{
using (var query = new PlQuery("age_group(" + age + ", G)"))
{
foreach (PlQueryVariables v in query.SolutionVariables)
age_group += v["G"].ToString();
}
}
catch (SbsSW.SwiPlCs.Exceptions.PlException exp)
{
throw new FaultException<PrologFault>(new PrologFault(exp.Source), exp.MessagePl);
}
//Check whether age_group is found or not
if (string.IsNullOrEmpty(age_group))
{
throw new FaultException<NoSolutionFoundFault>(new NoSolutionFoundFault("No solution found"), "Age specified exceeds the diagnosis range!");
}
else
{
Callback.RespondToUser(age_group, ResponseType.Age);
}
}
Method#3:
public void QuitProlog()
{
if (PlEngine.IsInitialized)
{
PlEngine.PlCleanup();
}
}
The client invokes the first method just fine and a result of the first query is successfully returned. When client tries to call the second method an exception is thrown with message (attempted to read or write protected memory) which causes the application to freeze. I checked the event viewer and this is what I get:
Application: w3wp.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
at SbsSW.SwiPlCs.SafeNativeMethods.PL_new_term_ref()
at SbsSW.SwiPlCs.PlQuery..ctor(System.String, System.String)
at SbsSW.SwiPlCs.PlQuery..ctor(System.String)
at PrologQueryService.PrologQueryService.DetermineAgeGroup(Int32)
I also tried to use the interface for a .NET project.
Looking in the official repository of the CSharp interface to SWI-Prolog I noticed that the project is very old and the latest updates do not seem included in the binaries available in the download page of the official website.
Then I did the following steps:
The contrib repository dedicated to .NET indicates that the compatible SWI-Prolog version (at the time of writing) is "8.0.3-1" (look in the README file).
-> Then I uninstalled from my computer the latest stable and installed the indicated one. I got it from the full list of downloads of the old versions at this link.
I cloned the SWI-Prolog/contrib-swiplcs repository, unloaded the incompatible projects from the solution, in my case, since I don't use Visual Studio.
-> I set the target framework to Net Framework 4.8 and recompiled it (you can also do this with standard NET). Beware of some pragma directives defined in the old project file (For example I re-defined _PL_X64 variable via code.
I brought the main unit test methods into a new project with xUnit wiht the appropriate changes.
I set the target to x64, recompiled and rebuilt the tests and the "hello world" example.
It worked!
I was able to use SWI-Prolog both for Net 4.8 and in other Net Core applications (if you make the needed changes in order to target the Net Standard). You should not have any problem in both cases).
This is my fork as a preliminary example.
Finally, I can load a *.pl Prolog file with a program in my C# application and use it to evaluate some business logic rules (example with boolean answer [Permitted/Not-Permitted]):
[Fact]
public void ShouldLoadAProgramAndUseIt()
{
var pathValues = Environment.GetEnvironmentVariable("PATH");
pathValues += #";C:\Program Files\swipl\bin";
Environment.SetEnvironmentVariable("PATH", pathValues);
// Positioning to project folder
var currentDirectory = Directory.GetCurrentDirectory().Split('\\').ToList();
currentDirectory.RemoveAll(r => currentDirectory.ToArray().Reverse().Take(3).Contains(r));
var basePath = currentDirectory.Aggregate((c1, c2) => $"{c1}\\{c2}");
var filePath = $"{basePath}\\prolog_examples\\exec_checker.pl";
String[] param = { "-q", "-f", filePath };
PlEngine.Initialize(param);
try
{
var query = "exutable('2020-08-15',[('monthly', ['2019-12-30', '2020-03-10'])])";
_testOutputHelper.WriteLine($"Query: {query}");
using (var q = new PlQuery(query))
{
var booleanAnswer = q.NextSolution();
_testOutputHelper.WriteLine($"Answer: {booleanAnswer}");
Assert.True(booleanAnswer);
}
query = "exutable('2020-08-15',[('daily', ['2019-12-30', '2020-08-15'])])";
_testOutputHelper.WriteLine($"Query: {query}");
using (var q = new PlQuery(query))
{
var booleanAnswer = q.NextSolution();
_testOutputHelper.WriteLine($"Answer: {booleanAnswer}");
Assert.False(booleanAnswer);
}
}
finally
{
PlEngine.PlCleanup();
}
}
Try to close engine in the end of the first method and initialize it in the second again.
You can check this as the answer to the question unless you object.
Hi I have created a solution with 3 projects in it. My solution has a Windows console app, a Xamarin Android project and a portable class library (PCL). I have referenced my PCL in both the Console and Android app. My PCL has my Models and ViewModels and i am calling a Web API from the viewModels. Here is a sample viewModel
public class RoutineViewModel
{
public async Task<IEnumerable<Routine>> GetRoutines()
{
//Lets Get List from Routines Web API
try
{
var client = new HttpClient();
var json = await client.GetStringAsync(BroadCastApiUrls.Routines);
var results = JsonConvert.DeserializeObject<IEnumerable<Routine>>(json);
if (results.Any())
return results;
return null;
}
catch (Exception ex)
{
//Some logging code here
return null;
}
}
}
I am making via an instance of this RoutineViewModel in a Method call from a button click handler, here is the code calling the GetRoutines Method
//In the Activity class
readonly RoutineViewModel routineViewModel = new RoutineViewModel();
private async Task<bool> LoadRoutines()
{
try
{
var routines = await routineViewModel.GetRoutines();
if (routines != null)
{
routinesList = routines.ToList();
listView.Adapter = new RoutinesListAdapter(this, routinesList);
}
else
{
var builder = new AlertDialog.Builder(this);
var alert = builder.Create();
alert.SetTitle("No Results!");
// alert.SetIcon(Resource.Drawable)
alert.SetMessage("No Routines From API!");
alert.DismissEvent += alert_DismissEvent;
alert.Show();
return true;
}
}
catch (Exception ex)
{
//Some logging and clean up code
}
return false;
}
This call returns the expected results from my console app but the same method call on my android simulator returns no results. What am i missing here? its driving me nuts.
Edit
From the loadRoutines click handler the call is
bool isLoaded = await LoadRoutines();
You're probably calling Task<T>.Result or Task.Wait further up the call stack, which will work on Console apps but fail in a GUI context. I explain this behavior in more detail on my blog.
The best solution is to use await instead of Result or Wait. Note that this means you'll have to decide what your UI looks like while the routines are loading, and then update it with the results when the loading completes.