SerialPort read in extension fails on dotnet error

438 views Asked by At

I'm writing an extension that reads serial port data. I'm running on BC14 OnPremis. Here is what I have. The global variable, running on client side (obviously serioal port is not on the server, with events because I need to receive data somehow.

var
    [RunOnClient]
    [WithEvents]
    Port: DotNet SerialPortA;

The trigger which is called when data on port is available. It is actually fired.

trigger Port::DataReceived(sender: Variant; e: DotNet SerialDataReceivedEventArgs)
var
    CurrPort: DotNet SerialPortA;
    BarText: DotNet String;
begin
    CurrPort := sender;
    BarText := CurrPort.ReadLine();
    Message(BarText);
end;

In the dotne.al file I have a declaration for SerialPort type. For some reasons it confused name with something else so I had to add a suffix to type alias. So now my type is called SerialPortA.

assembly("System")
{
    Version = '4.0.0.0';
    Culture = 'neutral';
    PublicKeyToken = 'b77a5c561934e089';

    type("System.IO.Ports.SerialPort"; "SerialPortA") { }
    type("System.IO.Ports.SerialDataReceivedEventArgs"; "SerialDataReceivedEventArgs") { }
    type("System.IO.Ports.SerialErrorReceivedEventArgs"; "SerialErrorReceivedEventArgs") { }
}

The problem is that a call to CurrPort.ReadLine() fails with error:

A call to System.Object.ReadLine failed with this message: The type of one or more arguments does not match the method's parameter type.

I tried to use another method Read. The error is the same. It is most probably related to Nav wrapping/unwrapping all dotnet variables to object type, but I don't know what ca I do with thath. Any guess what is going wrong here?

In the event log there is an error description

Server instance: N721
Category: Runtime
ClientSessionId: b6d2f654-0601-46b7-bd70-ae4fa637d422
ClientActivityId: 75ab980f-28d2-4a47-8d50-787c132c854c
ServerSessionUniqueId: cc752b06-1eab-43ed-8976-f1973da11ae5
ServerActivityId: fbc48c73-482c-4f84-87e3-4b3b1765dc5b
EventTime: 10/08/2020 06:29:21
Message   ObjectType: System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  MethodName: ReadLine
  BindingFlags: Instance, Public, InvokeMethod

ProcessId: 24200
Tag: 000010P
ThreadId: 199
CounterInformation: 

And another one

Server instance: N721
Category: Runtime
ClientSessionId: b6d2f654-0601-46b7-bd70-ae4fa637d422
ClientActivityId: 75ab980f-28d2-4a47-8d50-787c132c854c
ServerSessionUniqueId: cc752b06-1eab-43ed-8976-f1973da11ae5
ServerActivityId: fbc48c73-482c-4f84-87e3-4b3b1765dc5b
EventTime: 10/08/2020 06:29:21
Message (NavNCLDotNetInvokeException): This message had personal data removed. The original may still be in transient telemetry. Find it using the transientTelemetryId.
ParentException: NavNCLDotNetInvokeException
A call to System.Object.ReadLine failed with this message: The type of one or more arguments does not match the method's parameter type.
ExceptionStackTrace:
   at Microsoft.Dynamics.Nav.Runtime.NavApplicationMethod.InvokeMethod(ITreeObject obj, String methodName, Object[] args, Boolean resolveHandler, Boolean throwOnNotFound)
   at Microsoft.Dynamics.Nav.Service.NSField.InvokeEventTriggerInternal(NavSession con, String eventName)
   at Microsoft.Dynamics.Nav.Service.NSField.InvokeEventTrigger(NavSession session, String eventName)
   at SyncInvokeInvokeEventTrigger(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.RunInTransactionCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.TransientErrorRetryCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.ErrorMappingCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
InnerException:
ParentException: TargetInvocationException
ExceptionStackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Dynamics.Nav.Runtime.NavApplicationMethod.InvokeMethod(ITreeObject obj, String methodName, Object[] args, Boolean resolveHandler, Boolean throwOnNotFound)
InnerException:
RootException: NavNCLDotNetInvokeException
A call to System.Object.ReadLine failed with this message: The type of one or more arguments does not match the method's parameter type.
ExceptionStackTrace:
   at Microsoft.Dynamics.Nav.Types.NavAutomationHelper.GetMethodInfo(String methodName, Type objectType, BindingFlags binding, ParameterModifier[] modifier, Object[] arguments, Type[] referenceTypes)
   at Microsoft.Dynamics.Nav.Runtime.NavDotNet.Invoke[T](String methodName, UInt32 methodIndex, BindingFlags flags, ParameterModifier modifier, Type[] referenceTypes, Object[] arguments)
   at Microsoft.Dynamics.Nav.Runtime.NavDotNet.InvokeMethod[T](Boolean isStatic, String methodName, UInt32 methodIndex, Object[] arguments)
   at Microsoft.Dynamics.Nav.BusinessApplication.Page1057905.Porta58a58DataReceived_Scope.OnRun()
   at Microsoft.Dynamics.Nav.Runtime.NavMethodScope.Run()
   at Microsoft.Dynamics.Nav.BusinessApplication.Page1057905.Porta58a58DataReceived(NavVariant sender, NavDotNet e)
CallerStackTrace:
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.ErrorMappingCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.PushPopCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationTracer.TraceScopeCombinator(Category telemetryCategory, ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass10_0.<PerformanceCounterCombinator>b__0()
   at Microsoft.Dynamics.Nav.Runtime.NavPerformanceCounterSetter.UpdatePerformanceCountersWithAverageServiceOperationDuration(Stopwatch stopWatch, Action action)
   at Microsoft.Dynamics.Nav.Runtime.NavPerformanceCounterSetter.UpdatePerformanceCountersWithAverageServiceOperationAction(Action action, NavSession session)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.PerformanceCounterCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.InitClientTelemetryIdsCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.TlsClearCombinator(ServiceOperation innerOperation, NSServiceBase serviceInstance, MethodBase syncMethod, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.<>c__DisplayClass28_1.<Combine>b__1(NSServiceBase serviceInstance, Object[] inputs, Object[]& outputs)
   at Microsoft.Dynamics.Nav.Service.ServiceOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
   at System.ServiceModel.Dispatcher.MessageRpc.Wrapper.Resume(Boolean& alreadyResumedNoLock)
   at System.ServiceModel.Dispatcher.ThreadBehavior.ResumeProcessing(IResumeMessageRpc resume)
   at Microsoft.Dynamics.Nav.Runtime.NavSynchronizationContext.<>c__DisplayClass1_0.<ClearThreadLocalStorageDelegate>b__0(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()


ProcessId: 24200
Tag: 00000HC
ThreadId: 199
CounterInformation: 
1

There are 1 answers

0
kaspermoerch On

I don't have a serial port to test with, so this is of the top of my mind.

You probably need to cast the variant to the correct type:

trigger Port::DataReceived(sender: Variant; e: DotNet SerialDataReceivedEventArgs)
var
    Type: DotNet Type;
    Convert: DotNet Convert;
    CurrPort: DotNet SerialPortA;
    BarText: DotNet String;
begin
    // This check is just best pratice, but not needed in this case as we know sender is a SerialPort.
    if not sender.IsDotNet() then
      exit;

    Type := GetDotNetType(sender);
    CurrPort := Convert.ChangeType(sender, Type);
    BarText := CurrPort.ReadLine();
    Message(BarText);
end;