I'm trying to call c# static member function from erlang using nfx. I follow doc http://blog.aumcode.com/2013/10/nfx-native-interoperability-of-net-with.html and I am able to call erlang:now from C# and System.DateTime.UtcNow from erlang. However when I try to call non-System from erlang, I get error
** exception error: no match of right hand side value {error,
"unknown type: 'Program'"}
C# code:
namespace erlangIntegration {
public class Program {
static void Main(string[] args) {
var n = new ErlLocalNode("abc", new ErlAtom("'asdf'"));
n.AcceptConnections = false;
n.Start();
var m = n.CreateMbox("test");
var r = m.RPC("r#myPC", "erlang", "now", new ErlList());
Console.WriteLine("Remote time: {0}", r.ValueAsDateTime.ToString());
System.Console.WriteLine("My simple rpc: " + Program.FixedDate());
System.Console.WriteLine("Done. Press any key to exit.");
System.Console.ReadKey();
}
public static DateTime FixedDate() {
return new DateTime(2000, 12, 12);
}
}
}
erlang calls:
(r#myPC)2> f(Time), {ok, Time} = rpc:call('abc#myPC', 'System.DateTime', 'UtcNow', []), calendar:now_to_local_time(Time).
Above call works returning {{2016,1,18},{11,33,17}}
However
(r#myPC)12> f(Time), {ok, Time} = rpc:call('abc#myPC', 'Program', 'FixedDate', []), calendar:now_to_local_time(Time).
returns error:
** exception error: no match of right hand side value {error,
"unknown type: 'Program'"}
I also tried erlangIntegration.Program and to use System namespace.
How to properly make rpc call to c#?
Erlang requires assembly qualified name as input in rpc:call. So instead of 'Program' output of the following code has to be used:
var obj = new Program();
//type from here has to be used in Erlang's call instead of 'Program'
System.Console.WriteLine("Type: " + obj.GetType().AssemblyQualifiedName);
Related
We are using a C# application to read variables from a Beckhoff PLC through TwinCAT ADS v.3. If we attempt to utilize the same code to read properties the code fails with an exception.
FUNCTION_BLOCK FB_Sample
VAR
SomeVariable : INT;
END_VAR
PROPERTY SomeProp : INT // declared in a separate file
// Code used to read variable (symbol)
var handle = client.CreateVariableHandle("sampleProgram.Source.SomeVariable");
var result = client.ReadAny(handle, typeof(int));
client.DeleteVariableHandle(handle);
// Adapted code used to read property (not a symbol)
var handle = client.CreateVariableHandle("sampleProgram.Source.SomeProp"); // This fails
var result = client.ReadAny(handle, typeof(int));
client.DeleteVariableHandle(handle);
When trying to create a variable handle using the above code, we receive TwinCAT.Ads.AdsErrorException: 'Ads-Error 0x710 : Symbol could not be found.'.
Since we knew that METHOD must be marked with {attribute 'TcRpcEnable'} so it can be called with this code:
client.InvokeRpcMethod("{symbolPath}", "{methodName}", {parameters} });
We attempted to use that attribute {attribute 'TcRpcEnable'} on the property as well. Using TcAdsClient.CreateSymbolLoader and looping over all available symbols we discovered that the getter/setter of the property were then marked as rpc-methods.
Console.WriteLine($"Name: {rpcMethod.Name}; Parameters.Count: {rpcMethod.Parameters.Count}; ReturnType: {rpcMethod.ReturnType};");
RpcMethods: 2
Name: __setSomeProp; Parameters.Count: 1; ReturnType: ;
Name: __getSomeProp; Parameters.Count: 0; ReturnType: INT;
But try as we might, we cannot invoke the rpc method:
var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "__getSomeProp", Array.Empty<object>());
// Throws: TwinCAT.Ads.AdsErrorException: 'Ads-Error 0x710 : Symbol could not be found.'
var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "__get{SomeProp}", Array.Empty<object>());
// Throws: TwinCAT.Ads.RpcMethodNotSupportedException: 'The RPC method '__get{SomeProp}' is not supported on symbol 'sampleProgram.Source!'
var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "get{SomeProp}", Array.Empty<object>());
// Throws: TwinCAT.Ads.RpcMethodNotSupportedException: 'The RPC method 'get{SomeProp}' is not supported on symbol 'sampleProgram.Source!'
var propertyResult = client.InvokeRpcMethod("sampleProgram.Source.SomeProp", "get", Array.Empty<object>());
// Throws: System.ArgumentNullException: 'Value cannot be null.
// Parameter name: symbol'
Any suggestion on how we can read/write variables defined as properties on function blocks?
When you define a new property you automatically create a get and a set for that property.
You normally use properties to read or write variables that are in the VAR section of the function block.
All variables that are in the VAR section are private thus the need of properties to access those VARs from outside the Function Block.
Properties in theory should not do any complex calculations or run any logic unlike Methods.
The point I want to make is that you do not need and should not call properties via ADS.
You have access to all private VARs via ADS anyway so there is no need to call properties through ADS in the first place.
#Edit
I'm still of the opinion that properties should not contain any logic and therefor there is no need to call them via ADS.
Nevertheless there are always exceptions.
Be aware that according to the Beckhoff documentation, only simple data types and pointers will work, not structures.
Moreover "Function monitoring is not possible in the compact runtime system".
Here my working example after experimenting with the {attribute 'monitoring' := 'call'} attribute
In Twincat:
{attribute 'monitoring' := 'call'}
PROPERTY RemoteCall : INT
GET:
RemoteCall := buffer;
SET:
buffer := buffer + RemoteCall;
In C#
class Program
{
static TcAdsClient tcClient;
static void Main(string[] args)
{
tcClient = new TcAdsClient();
tcClient.Connect(851);
AdsStream dataStream = new AdsStream(2);
int iHandle = tcClient.CreateVariableHandle("MAIN.fbTest.RemoteCall");
tcClient.Read(iHandle, dataStream);
Console.WriteLine("Remote Var before property call: " + BitConverter.ToInt16(dataStream.ToArray(), 0));
tcClient.WriteAny(iHandle,Convert.ToInt16(2));
tcClient.Read(iHandle, dataStream);
Console.WriteLine("Remote Var after property call: " + BitConverter.ToInt16(dataStream.ToArray(), 0));
Console.WriteLine();
Console.ReadLine();
}
}
According to Stefan Hennecken on his Blog the property has to be decorated with a pragma to enable this:
{attribute ‘monitoring’ := ‘call’}
PROPERTY PUBLIC nProp : BYTE
Then it can be read/written with this code sample:
using (AdsClient client = new AdsClient())
{
byte valuePlc;
client.Connect(AmsNetId.Local, 851);
valuePlc = (byte)client.ReadValue(“MAIN.fbFoo.nProp”, typeof(byte));
client.WriteValue(“MAIN.fbFoo.nProp”, ++valuePlc);
}
I get this error message:
Error 1 No overload for method 'Feval' takes 2 arguments
My matlab function which i call in c# has only one input argument (txt-File)! If i use the command "Feval" it says i need 2 arguments... But which 2arguments? I have only one input parameter... Thank you
The problem:
//matlab.Feval("test_2",input,res); -> Trouble
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// Create the MATLAB instance
MLApp.MLApp matlab = new MLApp.MLApp();
// Change to the directory where the function is located
matlab.Execute(#"cd C:\Users\z003dukj\Documents\MATLAB\test_2");
string[] input = System.IO.File.ReadAllLines(#"C:\Users\z003dukj\Documents\MATLAB\aaaa.txt");
// Define the output
object result = null;
// Call the MATLAB function myfunc
matlab.Feval("test_2",input);
// Display result
object[] res = result as object[];
Console.WriteLine(res[0]);
Console.WriteLine(res[1]);
Console.ReadLine();
}
}
}
The second input to Feval should be the expected number of output arguments of your custom function. In your case, that appears to be zero, so your call to Feval should be
matlab.Feval("test_2", 0, input);
I've been uniting testing two simple examples of compiling CSharpValue activities. One works and the other doesn't I can't figure out why. If someone could point out the issue and optionally a change to correct it if possible.
Details:
The first unit test works SequenceActivityCompile() the second CodeActivityCompile fails with a NotSupportedException (Expression Activity type CSharpValue requires compilation in order to run. Please ensure that the workflow has been compiled.)
I heard somewhere this can be related to ForImplementation but CodeActivityCompile has the same error whether its value is true or false.
This example is a basic adaption of the Microsoft example at: https://msdn.microsoft.com/en-us/library/jj591618(v=vs.110).aspx
This example blog post discussing compiling C# expressions in WF 4+ at length. If anyone reaching this question needs a basic introduction to the topic:
http://blogs.msdn.com/b/tilovell/archive/2012/05/25/wf4-5-using-csharpvalue-lt-t-gt-and-csharpreference-lt-t-gt-in-net-4-5-compiling-expressions-and-changes-in-visual-studio-generated-xaml.aspx
Related Code:
[TestMethod]
public void SequenceActivityCompile()
{
Activity sequence = new Sequence
{
Activities = { new CSharpValue<string>("\"Hello World \"") }
};
CompileExpressions(sequence);
var result = WorkflowInvoker.Invoke(sequence);
}
[TestMethod]
public void CodeActivityCompile()
{
var code = new CSharpValue<String>("\"Hello World\"");
CompileExpressions(code);
var result = WorkflowInvoker.Invoke(code);
}
void CompileExpressions(Activity activity)
{
// activityName is the Namespace.Type of the activity that contains the
// C# expressions.
string activityName = activity.GetType().ToString();
// Split activityName into Namespace and Type.Append _CompiledExpressionRoot to the type name
// to represent the new type that represents the compiled expressions.
// Take everything after the last . for the type name.
//string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
string activityType = "TestType";
// Take everything before the last . for the namespace.
//string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());
string activityNamespace = "TestSpace";
// Create a TextExpressionCompilerSettings.
TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
{
Activity = activity,
Language = "C#",
ActivityName = activityType,
ActivityNamespace = activityNamespace,
RootNamespace = null,
GenerateAsPartialClass = false,
AlwaysGenerateSource = true,
ForImplementation = false
};
// Compile the C# expression.
TextExpressionCompilerResults results =
new TextExpressionCompiler(settings).Compile();
// Any compilation errors are contained in the CompilerMessages.
if (results.HasErrors)
{
throw new Exception("Compilation failed.");
}
// Create an instance of the new compiled expression type.
ICompiledExpressionRoot compiledExpressionRoot =
Activator.CreateInstance(results.ResultType,
new object[] { activity }) as ICompiledExpressionRoot;
// Attach it to the activity.
System.Activities.Expressions.CompiledExpressionInvoker.SetCompiledExpressionRoot(
activity, compiledExpressionRoot);
}
I've been looking at some code in a debugger associated with Razor View engine and I noticed that some of the types appear in Debugger with a trailing dot character at the end of the type name e.g.:
{Nancy.ViewEngines.Razor.RazorViewEngine.}
Does anyone know what this indicates? It's not valid syntax to use it when specifying a cast on an object so I'm intrigued as to what it indicates within the debugger.
EDIT: As requested by #Damien_The_Unbeliever, screenshot of the variable in debugger:
And the code that I'm looking at:
public TCompiledView GetOrAdd<TCompiledView>(
ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
TCompiledView compiledView = default(TCompiledView);
compiledView = (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x));
To give a little more background, we're trying to add logging to our Nancy View Cache to investigate an intermittent issue with Razor Views throwing compilation errors, but that isn't really relevant to the question.
I've seen this happen when the variable/value is actually of a compiler generated type (e.g. for holding the "local variables" captured by a lambda, async, iterator, etc). The debugger (in various places) seems unable to display the actual class name.
E.g. this example program:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.DoStuff();
}
void DoStuff()
{
int i = 19;
Expression<Func<int>> j = () => i + 10;
var k = (((j.Body as BinaryExpression).Left as MemberExpression).Expression as ConstantExpression).Value;
Console.ReadLine();
}
}
With a breakpoint on Console.ReadLine(), you'll find the k class's type looks like Program. rather than Program+<>_DisplayClass0
Addition by Jeppe: This example is a slight simplification of the above, avoiding the expression tree. Looks at a delegate instance's Target which will be an instance of a generated class. For comparison also looks at an iterator block type:
using System;
using System.Collections.Generic;
static class Program
{
static void Main()
{
int i = 19; // to be captured by lambda, will become field on a generated class
Func<int> f = () => i;
var target = f.Target; // when debugging type looks like "Program."
Console.WriteLine(target.GetType().ToString()); // writes "Program+<>c__DisplayClass1"
var seq = GetSeq(); // when debugging type looks like "Program.GetSeq"
Console.WriteLine(seq.GetType().ToString()); // writes "Program+<GetSeq>d__3"
}
static IEnumerable<int> GetSeq() // returns "state machine" (iterator block)
{
yield return 42;
}
}
I am calling javascript functions from C# using the WPF variant of the browser control via InvokeScript.
I can call my function once without any problems. But when I call it a second time, it throws the following error :
Unknown name. (Exception from HRESULT: 0x80020006
(DISP_E_UNKNOWNNAME))
The Code I am using is the following :
this.browser.LoadCompleted += (sender, args) =>
{
this.browser.InvokeScript("WriteFromExternal", new object[] { "firstCall" }); // works
this.browser.InvokeScript("WriteFromExternal", new object[] { "secondCall" }); // throws error
};
The javascript function is :
function WriteFromExternal(message) {
document.write("Message : " + message);
}
I can call C# functions from the page via javascript just fine and invoke from C#, just can't invoke a second time. Regardless of what function I call.
I do not understand why it would fail the second time.
Thank you
Edit :
Did the following test (javascript) :
function pageLoaded() {
window.external.tick();
window.external.tick();
window.external.tick();
}
window.onload = pageLoaded;
function WriteFromExternal(message) {
document.write("Message : " + message);
}
And this is the C# side :
private int i = 0;
public void tick()
{
invoke("WriteFromExternal", new object[] { "ticked"+ i++ });
}
public static void invoke(string method, object[] parameters)
{
mainInterface.browser.InvokeScript(method, parameters);
}
And still throws the same error (after the first call), this suggests that it does not matter from where it is called, invoking the function from C# will throw this error if done more than once.
I assume you did the same as me and put your scripts in the body. For some reason when you call document.write from wpf it completely overwrites the document. If instead of using document.write you append a child it works fine. So change your JavaScript function to be:
window.WriteFromExternal = function (message) {
var d = document.createElement("div")
d.innerHTML= "Message : " + message;
document.body.appendChild(d);
}
// call from c#
WriteFromExternal("test")
It's been a while since I did something similar, but from what I remember your code looks correct. However, I do remember using a slightly different pattern in my project. Instead of delegating back to a JS method on the page I would make my ScriptingHost methods return values
EX:
C#:
public string tick()
{
return "some stuff";
}
var msg = window.external.tick();
document.write(msg);
If you have more complex objects than simple strings you can serialize them to JSON and parse them into an object on the JS side.
var jsonObj = JSON.parse(window.external.someMethod());
Not sure if you have the luxury of being able to change your method signatures in your scripting object, but it's at least an alternative approach.
Also, in your current implementation, have you tried to do something other than document.write? Do you get the same error if you display an alert box?