progress on transfer loop.

This commit is contained in:
Eugene Wang
2021-04-28 07:19:02 -04:00
parent c8e101dd0d
commit 4d17226711
5 changed files with 260 additions and 76 deletions

View File

@@ -11,7 +11,7 @@ namespace Net5Console
class Program
{
[STAThread]
static void Main(string[] args)
static void Main()
{
Console.WriteLine("Starting twain test in console...");
Console.WriteLine();
@@ -21,29 +21,21 @@ namespace Net5Console
{
session.DeviceEvent += (sender, e) =>
{
Console.WriteLine($"Got device event " + (TWDE)e.Event);
Console.WriteLine($"Got device event " + e.Event);
Console.WriteLine();
};
session.ScanEvent += (sender, closing) =>
session.TransferError += (sender, e) =>
{
Console.WriteLine($"Got scan event " + closing);
Console.WriteLine($"Transfer error {e.Code} {e.Exception}.");
};
session.TransferReady += (sender, e) =>
{
Console.WriteLine($"Transfer ready, count={e.PendingCount}.");
};
session.SourceDisabled += (sender,e)=>
{
Console.WriteLine("Disabled device.");
Console.WriteLine();
// don't care, just end it
TW_PENDINGXFERS pending = default;
var sts = session.TWAIN.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
TW_USERINTERFACE twuserinterface = default;
if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS)
{
Console.WriteLine("Disabled device.");
Console.WriteLine();
}
else
{
Console.Error.WriteLine("Failed to disabled device.");
Console.WriteLine();
}
hold.Set();
};
@@ -135,6 +127,11 @@ namespace Net5Console
}
private static void Session_SourceDisabled(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private static void WriteCapInfo(Capabilities caps, CAP cap)
{
// use reflection due to unknown generics

View File

@@ -10,20 +10,8 @@ namespace NTwain
/// <summary>
/// Some utility extension methods.
/// </summary>
public static class ExtensionMethods
static class ExtensionMethods
{
/// <summary>
/// Checks if the returned <see cref="STS"/> contains a particular
/// <see cref="STS"/> value since normal TWAIN
/// return code is merged with condition code into <see cref="STS"/>.
/// This is only useful for non-success values.
/// </summary>
/// <param name="status"></param>
/// <param name="checkFor">Non-success code to check for.</param>
/// <returns></returns>
public static bool Is(this STS status, STS checkFor)
{
return (status & checkFor) == checkFor;
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using TWAINWorkingGroup;
namespace NTwain
{
/// <summary>
/// Contains TWAIN codes and source status when an error is encountered during transfer.
/// </summary>
public class TransferErrorEventArgs : EventArgs
{
public TransferErrorEventArgs(STS code, Exception error = null)
{
Code = code;
Exception = error;
}
/// <summary>
/// Gets the error code from the transfer.
/// </summary>
public STS Code { get; }
/// <summary>
/// Gets the exception if the error is from some exception.
/// </summary>
public Exception Exception { get; }
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TWAINWorkingGroup;
namespace NTwain
{
/// <summary>
/// Contains event data when a data transfer is ready to be processed.
/// </summary>
public class TransferReadyEventArgs : EventArgs
{
public TransferReadyEventArgs(TWAIN twain, int pendingCount, TWEJ endOfJobFlag)
{
_twain = twain;
PendingCount = pendingCount;
EndOfJobFlag = endOfJobFlag;
}
/// <summary>
/// Gets or sets whether the current transfer should be skipped
/// and continue next transfer if there are more data.
/// </summary>
public bool SkipCurrent { get; set; }
/// <summary>
/// Gets or sets whether to cancel the capture phase.
/// </summary>
public CancelType CancelCapture { get; set; }
/// <summary>
/// Gets the end of job flag value for this transfer if job control is enabled.
/// </summary>
public TWEJ EndOfJobFlag { get; private set; }
/// <summary>
/// Gets the known pending transfer count. This may not be appilicable
/// for certain scanning modes.
/// </summary>
public int PendingCount { get; private set; }
TW_IMAGEINFO? _imgInfo;
private readonly TWAIN _twain;
/// <summary>
/// Gets the tentative image information for the current transfer if applicable.
/// This may differ from the final image depending on the transfer mode used (mostly when doing mem xfer).
/// </summary>
public TW_IMAGEINFO? PendingImageInfo
{
get
{
if (!_imgInfo.HasValue)
{
TW_IMAGEINFO info = default;
if (_twain.DatImageinfo(DG.IMAGE, MSG.GET, ref info) == STS.SUCCESS)
{
_imgInfo = info;
}
}
return _imgInfo;
}
}
}
public enum CancelType
{
/// <summary>
/// No cancel.
/// </summary>
None,
/// <summary>
/// Stops feeder but continue receiving scanned images.
/// </summary>
Graceful,
/// <summary>
/// Stops feeder and discard pending images.
/// </summary>
Immediate
}
}

View File

@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TWAINWorkingGroup;
@@ -80,8 +82,20 @@ namespace NTwain
/// </summary>
public event EventHandler SourceDisabled;
/// <summary>
/// Raised when there's some error during transfer.
/// </summary>
public event EventHandler<TransferErrorEventArgs> TransferError;
/// <summary>
/// Raised when there's a pending transfer. Can be used to cancel transfers.
/// </summary>
public event EventHandler<TransferReadyEventArgs> TransferReady;
private void HandleUIThreadAction(Action action)
{
DebugThreadInfo("begin");
_threadMarshaller.Invoke(action);
}
@@ -93,6 +107,8 @@ namespace NTwain
// Drain the event queue...
while (true)
{
DebugThreadInfo("in loop");
// Try to get an event...
twdeviceevent = default;
sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent);
@@ -116,23 +132,103 @@ namespace NTwain
private STS HandleScanEvent(bool closing)
{
DebugThreadInfo("begin");
// the scan event needs to return asap since it can come from msg loop
// so fire off the handling work to another thread
_threadMarshaller.BeginInvoke(new Func<bool, STS>(HandleScanEventReal), closing);
_threadMarshaller.BeginInvoke(new Action<bool>(HandleScanEventReal), closing);
return STS.SUCCESS;
}
// tracks transfer state
bool _xferReadySent;
bool _disableDsSent;
void HandleScanEventReal(bool closing)
{
//// Scoot...
//if (_twain == null) return STS.FAILURE;
DebugThreadInfo("begin");
//// We're superfluous...
//if (_twain.GetState() <= STATE.S4 || closing) return STS.SUCCESS;
if (_twain == null || State <= STATE.S4 || closing) return;
if (_twain.IsMsgCloseDsReq() || _twain.IsMsgCloseDsOk())
{
StepDown(STATE.S4);
return;
}
// all except mem xfer will run this once and raise event.
// mem xfer will run this multiple times until complete image is assembled
if (_twain.IsMsgXferReady())
{
TW_PENDINGXFERS pending = default;
var sts = _twain.DatPendingxfers(DG.CONTROL, MSG.GET, ref pending);
if (sts != STS.SUCCESS)
{
try
{
TransferError?.Invoke(this, new TransferErrorEventArgs(sts));
}
catch { }
return; // do more?
}
var xferMech = Capabilities.ICAP_XFERMECH.GetCurrent();
var readyArgs = new TransferReadyEventArgs(_twain, pending.Count, (TWEJ)pending.EOJ);
try
{
TransferReady?.Invoke(this, readyArgs);
}
catch { }
if (readyArgs.CancelCapture == CancelType.Immediate)
{
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
}
else
{
if (readyArgs.CancelCapture == CancelType.Graceful) StopCapture();
if (!readyArgs.SkipCurrent)
{
switch (xferMech)
{
case TWSX.NATIVE:
RunImageNativeXfer();
break;
case TWSX.MEMFILE:
RunImageMemFileXfer();
break;
case TWSX.FILE:
RunImageFileXfer();
break;
case TWSX.MEMORY:
RunImageMemoryXfer();
break;
}
}
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref pending);
}
// TODO: may be wrong for now
if (pending.Count == 0 || sts == STS.CANCEL || sts == STS.XFERDONE)
{
StepDown(STATE.S4);
}
else
{
HandleScanEvent(State <= STATE.S3);
}
}
}
[Conditional("DEBUG")]
private void DebugThreadInfo(string description, [CallerMemberName] string callerName = "")
{
var tid = Thread.CurrentThread.ManagedThreadId;
Debug.WriteLine($"[Thread {tid}] {callerName}() {description}");
}
private void RunImageMemoryXfer()
{
throw new NotImplementedException();
//// Handle DAT_NULL/MSG_XFERREADY...
//if (_twain.IsMsgXferReady() && !_xferReadySent)
@@ -161,20 +257,6 @@ namespace NTwain
// }
//}
//// Handle DAT_NULL/MSG_CLOSEDSREQ...
//if (_twain.IsMsgCloseDsReq() && !_disableDsSent)
//{
// _disableDsSent = true;
// StepDown(STATE.S4);
//}
//// Handle DAT_NULL/MSG_CLOSEDSOK...
//if (_twain.IsMsgCloseDsOk() && !_disableDsSent)
//{
// _disableDsSent = true;
// StepDown(STATE.S4);
//}
//// This is where the statemachine runs that transfers and optionally
//// saves the images to disk (it also displays them). It'll go back
//// and forth between states 6 and 7 until an error occurs, or until
@@ -183,13 +265,21 @@ namespace NTwain
//{
// CaptureImages();
//}
}
private void RunImageFileXfer()
{
throw new NotImplementedException();
}
private void RunImageMemFileXfer()
{
throw new NotImplementedException();
}
private void RunImageNativeXfer()
{
//// Trigger the next event, this is where things all chain together.
//// We need begininvoke to prevent blockking, so that we don't get
//// backed up into a messy kind of recursion. We need DoEvents,
//// because if things really start moving fast it's really hard for
//// application events, like button clicks to break through...
//HandleScanEvent(_twain.GetState() <= STATE.S3);
}
//protected virtual void OnScanEvent(bool closing) { }
@@ -301,34 +391,31 @@ namespace NTwain
/// <summary>
/// Steps down the TWAIN state to the specified state.
/// </summary>
/// <param name="state"></param>
public void StepDown(STATE state)
/// <param name="target"></param>
public void StepDown(STATE target)
{
TW_PENDINGXFERS twpendingxfers = default;
// Make sure we have something to work with...
if (_twain == null)
{
return;
}
if (_twain == null) return;
// Walk the states, we don't care about the status returns. Basically,
// these need to work, or we're guaranteed to hang...
// 7 --> 6
if ((_twain.GetState() == STATE.S7) && (state < STATE.S7))
if ((State == STATE.S7) && (target < STATE.S7))
{
TW_PENDINGXFERS twpendingxfers = default;
_twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers);
}
// 6 --> 5
if ((_twain.GetState() == STATE.S6) && (state < STATE.S6))
if ((State == STATE.S6) && (target < STATE.S6))
{
TW_PENDINGXFERS twpendingxfers = default;
_twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers);
}
// 5 --> 4
if ((_twain.GetState() == STATE.S5) && (state < STATE.S5))
if ((State == STATE.S5) && (target < STATE.S5))
{
TW_USERINTERFACE twuserinterface = default;
_twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface);
@@ -336,14 +423,14 @@ namespace NTwain
}
// 4 --> 3
if ((_twain.GetState() == STATE.S4) && (state < STATE.S4))
if ((State == STATE.S4) && (target < STATE.S4))
{
_caps = null;
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref _twain.m_twidentityDs);
}
// 3 --> 2
if ((_twain.GetState() == STATE.S3) && (state < STATE.S3))
if ((State == STATE.S3) && (target < STATE.S3))
{
_twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd);
}