I started developing an android AR app using Vuforia library a few days ago. I am able to overlay my custom image over pre-defined stones-and-chips target in the vuforia sample app provided. In Cloud Reco sample, it recognizes the target image and i am able to fetch the meta data for the same. How can I now overlay my custom image over this recognized target?
In the given User Defined Target sample, it creates an Image Target at runtime, and overlays my image, but i want it overlayed only on cloud-recognized targets.
So I tried integrating it in CloudReco.java, so that when an object is cloud-recognized, it builds a target out of it at runtime.
#Override
public void onVuforiaUpdate(State state)
{
// Get the tracker manager:
TrackerManager trackerManager = TrackerManager.getInstance();
// Get the object tracker:
ObjectTracker objectTracker = (ObjectTracker) trackerManager
.getTracker(ObjectTracker.getClassType());
// Get the target finder:
TargetFinder finder = objectTracker.getTargetFinder();
// Check if there are new results available:
final int statusCode = finder.updateSearchResults();
// Show a message if we encountered an error:
if (statusCode < 0)
{
boolean closeAppAfterError = (
statusCode == UPDATE_ERROR_NO_NETWORK_CONNECTION ||
statusCode == UPDATE_ERROR_SERVICE_NOT_AVAILABLE);
showErrorMessage(statusCode, state.getFrame().getTimeStamp(), closeAppAfterError);
} else if (statusCode == TargetFinder.UPDATE_RESULTS_AVAILABLE)
{
// Process new search results
if (finder.getResultCount() > 0)
{
TargetSearchResult result = finder.getResult(0);
// Check if this target is suitable for tracking:
if (result.getTrackingRating() > 0)
{
Trackable trackable = finder.enableTracking(result);
// Trying out the below method
// ==================================================
String targetMetaData = result.getMetaData();
Log.d("TargetMetaData", targetMetaData);
// Create the data set:
dataSetUserDef = objectTracker.createDataSet();
if (!objectTracker.activateDataSet(dataSetUserDef))
{
Log.d(LOGTAG, "Failed to activate data set.");
return;
}
Log.d(LOGTAG, "Successfully loaded and activated data set.");
// Build a target out of the selected image object
ImageTargetBuilder targetBuilder = objectTracker
.getImageTargetBuilder();
if (targetBuilder != null)
{
if (targetBuilder.getFrameQuality() == ImageTargetBuilder.FRAME_QUALITY.FRAME_QUALITY_LOW)
{
Log.d("Cloud Reco", "Low Frame Quality");
}
String name;
if (!targetBuilder.build("randomName", 320.0f)) {
Log.d("CloudReco", "could not build target");
}
}
// ===================================================
if (mExtendedTracking)
trackable.startExtendedTracking();
}
}
}
}
However I am getting the below error :`
03-10 08:17:15.330 291-28912/? D/ae_mgr: ae_mgr [doPvAEmonitor()][getPreviewFlare] i2FlareOffset = 27 i2FlareGain = 515
03-10 08:17:15.339 28797-28882/com.vuforia.samples.VuforiaSamples I/AR: Successfully created ImageTarget.
03-10 08:17:15.340 291-28927/? D/MtkCam/MtkCamUtils: [queryRawStride]imgFmt(269), imgWidth(1632), stride(1640)
03-10 08:17:15.341 291-28927/? I/iio/ifunc: [_config] path(2),CQ(4),en1(0x40300000),en2(0x38a00003),dma(0x00280080),fmt(0x00000106),ctl(0x00010050),tcm_en(0x98100003),isIspOn(0x1),muxS2(0xc0080308)
03-10 08:17:15.341 291-28927/? I/MdpMgr: [init] +,cqIndex(4),sceID(8)
03-10 08:17:15.341 28797-28882/com.vuforia.samples.VuforiaSamples D/TargetMetaData: {"id":1,"image_url":"\/home\/lakshay\/repos\/barium-repos\/scormREPO\/3\/1\/mobile\/5wAhJHu04Ht_DX1600_DY1600_CX800_CY480.png5wAhJHu04Ht_DX1600_DY1600_CX800_CY480.png"}
03-10 08:17:15.343 28797-28882/com.vuforia.samples.VuforiaSamples I/AR: ObjectTracker: Successfully created dataset
03-10 08:17:15.344 291-28913/? D/flash_mgr_m.cpp: flash_mgr_m.cpp isAFLampOn 0
03-10 08:17:15.345 28797-28882/com.vuforia.samples.VuforiaSamples D/CloudReco: Successfully loaded and activated data set.
03-10 08:17:15.346 28797-28882/com.vuforia.samples.VuforiaSamples E/AR: Could not initiate UserDef Target build process
03-10 08:17:15.346 28797-28882/com.vuforia.samples.VuforiaSamples E/AR: Not in scanning state or invalid frame quality reading of -1. Call build() again in scanning mode with valid frame quality values.
03-10 08:17:15.346 28797-28882/com.vuforia.samples.VuforiaSamples D/CloudReco: could not build target
03-10 08:17:15.355 28797-28810/com.vuforia.samples.VuforiaSamples I/BufferQueueProducer: [SurfaceTexture--1-28797-1](this:0x8731e000,id:1,api:4,p:291,c:28797) queueBuffer: slot 1 is dropped, handle=0x854ba300
I also tried tweaking the UserDefinedTargets.java so that before building the Image Target, the TargetFinder checks if it the object matches the cloud targets. But seems like the same image here is not matching.
Here is the tweaked code :
when camera is clicked :
// Button Camera clicked
public void onCameraClick(View v)
{
if (isUserDefinedTargetsRunning())
{
// Shows the loading dialog
loadingDialogHandler
.sendEmptyMessage(LoadingDialogHandler.SHOW_LOADING_DIALOG);
// Builds the new target
//startBuild();
//// Calling startBuild as an async task instead
// since it involves network calls
SyncIncoData syncIncoData = new SyncIncoData();
syncIncoData.execute(new String[] { null });
}
}
building targets :
void startBuild()
{
TrackerManager trackerManager = TrackerManager.getInstance();
ObjectTracker objectTracker = (ObjectTracker) trackerManager
.getTracker(ObjectTracker.getClassType());
if (objectTracker != null)
{
ImageTargetBuilder targetBuilder = objectTracker
.getImageTargetBuilder();
if (targetBuilder != null)
{
if (targetBuilder.getFrameQuality() == ImageTargetBuilder.FRAME_QUALITY.FRAME_QUALITY_LOW)
{
showErrorDialogInUIThread();
}
// Tried below code for cloud-reco on this object
// ======================================================
// Initialize target finder:
TargetFinder targetFinder = objectTracker.getTargetFinder();
// Start initialization:
if (targetFinder.startInit(kAccessKey, kSecretKey))
{
Log.d("TargetFind", "init");
targetFinder.waitUntilInitFinished();
}
Log.d("TargetFind", "init over");
int resultCode = targetFinder.getInitState();
if (resultCode != TargetFinder.INIT_SUCCESS)
{
if(resultCode == TargetFinder.INIT_ERROR_NO_NETWORK_CONNECTION)
{
Log.d("TargetFinder", "no net");
}
else
{
Log.d("TargetFinder", "no service");
}
Log.e(LOGTAG, "Failed to initialize target finder.");
return;
}
Log.d("TargetFinder", "validated");
// Get the target finder:
TargetFinder finder = objectTracker.getTargetFinder();
Log.d("TargetFinder", "found");
// Check if there are new results available:
final int statusCode = finder.updateSearchResults();
Log.d("Status Code", String.valueOf(statusCode));
//// status is coming 1 => TargetFinder.UPDATE_NO_REQUEST ////
// Show a message if we encountered an error:
if (statusCode < 0)
{
Log.d("CloudFind", "not found");
return;
}
else if (statusCode == TargetFinder.UPDATE_RESULTS_AVAILABLE) {
//// Not coming here ////
Log.d("CloudFind", "found");
// Process new search results
if (finder.getResultCount() > 0) {
TargetSearchResult result = finder.getResult(0);
String targetMetaData = result.getMetaData();
Log.d("TargetMetaData", targetMetaData);
// Check if this target is suitable for tracking:
// Only if the target is matched and suitable for tracking
// then the Image target should be built
if (result.getTrackingRating() > 0) {
String name;
do
{
name = "UserTarget-" + targetBuilderCounter;
Log.d(LOGTAG, "TRYING " + name);
targetBuilderCounter++;
} while (!targetBuilder.build(name, 320.0f));
refFreeFrame.setCreating();
}
}
}
}
}
}
async task :
private class SyncIncoData extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... ot) {
startBuild();
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
}
PS :
I am open to change the approach to achieve this result.
Just need to match the image against a target, which i have on Vuforia web db, and fetch an image from another server to overlay on it.
Related
I have inherited an older customization to the Purchase Receipts / PO302000 screen that I'm trying to upgrade, and it had customization code to import Lot/Serial nbrs from an Excel spreadsheet. It all seems to work alright, except that at the end, it errors out when pressing a button as follows:
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
Here's the entire code:
public virtual void importAllocations()
{
try
{
if (Base.transactions.Current != null)
{
var siteid = Base.transactions.Current.SiteID;
if (Base.splits.Select().Count == 0)
{
if (this.NewRevisionPanel.AskExt() == WebDialogResult.OK)
{
const string PanelSessionKey = "ImportStatementProtoFile";
PX.SM.FileInfo info = PX.Common.PXContext.SessionTyped<PXSessionStatePXData>().FileInfo[PanelSessionKey] as PX.SM.FileInfo;
System.Web.HttpContext.Current.Session.Remove(PanelSessionKey);
if (info != null)
{
byte[] filedata = info.BinData;
using (NVExcelReader reader = new NVExcelReader())
{
Dictionary<UInt32, string[]> data = reader.loadWorksheet(filedata);
foreach (string[] textArray in data.Values)
{
if (textArray[0] != GetInventoryCD(Base.transactions.Current.InventoryID))
{
throw (new Exception("InventoryID in file does not match row Inventory ID"));
}
else
{
//Find the location ID based on the location CD provided by the Excel sheet...
INLocation inloc = PXSelect<INLocation,
Where<INLocation.locationCD, Equal<Required<INLocation.locationCD>>,
And<INLocation.siteID, Equal<Required<INLocation.siteID>>>>>.Select(Base
, textArray[1]
, Base.transactions.Current.SiteID);
Base.splits.Insert(new POReceiptLineSplit()
{
InventoryID = Base.transactions.Current.InventoryID,
LocationID = inloc.LocationID, //Convert.ToInt32(textArray[1]), //Base.transactions.Current.LocationID,
LotSerialNbr = textArray[2],
Qty = Decimal.Parse(textArray[3])
});
}
}
}
}
}
}
}
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
}
catch (FileFormatException fileFormat)
{
// Acuminator disable once PX1053 ConcatenationPriorLocalization [Justification]
throw new PXException(String.Format("Incorrect file format. File must be of type .xlsx", fileFormat.Message));
}
catch (Exception ex)
{
throw ex;
}
}
Now, there seems to be no such button - and I have no idea what it would be called now, or if it even still exists. I don't even really know what this action did.
Any ideas?
Thanks much...
That logic has been moved into the PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension. That action was doing the following in the PX.Objects.PO.LSPOReceiptLine
// PX.Objects.PO.LSPOReceiptLine
// Token: 0x0600446F RID: 17519 RVA: 0x000EE86C File Offset: 0x000ECA6C
public override IEnumerable BinLotSerial(PXAdapter adapter)
{
if (base.MasterCache.Current != null)
{
if (!this.IsLSEntryEnabled((POReceiptLine)base.MasterCache.Current))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
this.View.AskExt(true);
}
return adapter.Get();
}
Now it is called ShowSplits and is part of the POReceiptLineSplittingExtension extension.
// PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension
// Token: 0x06005359 RID: 21337 RVA: 0x00138621 File Offset: 0x00136821
public override IEnumerable ShowSplits(PXAdapter adapter)
{
if (base.LineCurrent == null)
{
return adapter.Get();
}
if (!this.IsLSEntryEnabled(base.LineCurrent))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
return base.ShowSplits(adapter);
}
Given the fact that ShowSplits is defined in the LineSplittingExtension originally it may be referred to as "LineSplittingExteions_ShowSplits" or "POReceiptLineSplittingExtension_ShowSplits". I would suggest including that POReceiptLineSplittingExtension as part of your extension and simply call the Base1.ShowSplits
The program returns: CANCELED: Reason=Error ErrorDetails=WebSocket Upgrade failed with an authentication error (401). Please check for correct subscription key (or authorization token) and region name. SessionId: cbfcdf7f26304343a08de6c398652053
I'm using my free trial subscription key and westus region. This is the sample code found here: https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/quickstarts/speech-to-text-from-microphone?tabs=unity%2Cx-android%2Clinux%2Cjava-runtime&pivots=programming-language-csharp
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;
#if PLATFORM_ANDROID
using UnityEngine.Android;
#endif
#if PLATFORM_IOS
using UnityEngine.iOS;
using System.Collections;
#endif
public class Helloworld : MonoBehaviour
{
// Hook up the two properties below with a Text and Button object in your UI.
public Text outputText;
public Button startRecoButton;
private object threadLocker = new object();
private bool waitingForReco;
private string message;
private bool micPermissionGranted = false;
#if PLATFORM_ANDROID || PLATFORM_IOS
// Required to manifest microphone permission, cf.
// https://docs.unity3d.com/Manual/android-manifest.html
private Microphone mic;
#endif
public async void ButtonClick()
{
// Creates an instance of a speech config with specified subscription key and service region.
// Replace with your own subscription key and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("yourSubscriptionKey", "yourRegion");
// Make sure to dispose the recognizer after use!
using (var recognizer = new SpeechRecognizer(config))
{
lock (threadLocker)
{
waitingForReco = true;
}
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
var result = await recognizer.RecognizeOnceAsync().ConfigureAwait(false);
// Checks result.
string newMessage = string.Empty;
if (result.Reason == ResultReason.RecognizedSpeech)
{
newMessage = result.Text;
}
else if (result.Reason == ResultReason.NoMatch)
{
newMessage = "NOMATCH: Speech could not be recognized.";
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = CancellationDetails.FromResult(result);
newMessage = $"CANCELED: Reason={cancellation.Reason} ErrorDetails={cancellation.ErrorDetails}";
}
lock (threadLocker)
{
message = newMessage;
waitingForReco = false;
}
}
}
void Start()
{
if (outputText == null)
{
UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
}
else if (startRecoButton == null)
{
message = "startRecoButton property is null! Assign a UI Button to it.";
UnityEngine.Debug.LogError(message);
}
else
{
// Continue with normal initialization, Text and Button objects are present.
#if PLATFORM_ANDROID
// Request to use the microphone, cf.
// https://docs.unity3d.com/Manual/android-RequestingPermissions.html
message = "Waiting for mic permission";
if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Permission.RequestUserPermission(Permission.Microphone);
}
#elif PLATFORM_IOS
if (!Application.HasUserAuthorization(UserAuthorization.Microphone))
{
Application.RequestUserAuthorization(UserAuthorization.Microphone);
}
#else
micPermissionGranted = true;
message = "Click button to recognize speech";
#endif
startRecoButton.onClick.AddListener(ButtonClick);
}
}
void Update()
{
#if PLATFORM_ANDROID
if (!micPermissionGranted && Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#elif PLATFORM_IOS
if (!micPermissionGranted && Application.HasUserAuthorization(UserAuthorization.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#endif
lock (threadLocker)
{
if (startRecoButton != null)
{
startRecoButton.interactable = !waitingForReco && micPermissionGranted;
}
if (outputText != null)
{
outputText.text = message;
}
}
}
}
The sample code you pasted above still has the placeholder values for region and subscription key. Just double checking that you did in fact replace those strings with your own subscription key and region? If that's true, can you please turn on logging, run the code again, and then provide the log? We can help diagnose from there...
To turn on logging, see https://aka.ms/speech/logging.
I am working on a Windows Universal App. I Want to get the Data from a Bluetooth Device to the Windows Phone. I am Using the Concept of RFCommCommunicationTrigger for this Purpose.
Here's the code Snippet I am Using
var rfTrigger = new RfcommConnectionTrigger();
// Specify what the service ID is
rfTrigger.InboundConnection.LocalServiceId = RfcommServiceId.FromUuid(new Guid("<some_base_guid>"));
//Register RFComm trigger
var rfReg = RegisterTaskOnce(
"HWRFCommTrigger",
"BackgroundLibrary.RFBackgroundTask",
rfTrigger, null
);
SetCompletedOnce(rfReg, OnTaskCompleted);
Here the Function of RegisterTaskOnce
static private IBackgroundTaskRegistration RegisterTaskOnce(string taskName, string entryPoint, IBackgroundTrigger trigger, params IBackgroundCondition[] conditions)
{
// Validate
if (string.IsNullOrEmpty(taskName)) throw new ArgumentException("taskName");
if (string.IsNullOrEmpty(entryPoint)) throw new ArgumentException("entryPoint");
if (trigger == null) throw new ArgumentNullException("trigger");
// Look to see if the name is already registered
var existingReg = (from reg in BackgroundTaskRegistration.AllTasks
where reg.Value.Name == taskName
select reg.Value).FirstOrDefault();
Debug.WriteLine("Background task "+ taskName+" is already running in the Background");
// If already registered, just return the existing registration
if (existingReg != null)
{
return existingReg;
}
// Create the builder
var builder = new BackgroundTaskBuilder();
builder.TaskEntryPoint = entryPoint;
builder.Name = taskName;
builder.SetTrigger(trigger);
// Conditions?
if (conditions != null)
{
foreach (var condition in conditions)
{
builder.AddCondition(condition);
}
}
// Register
return builder.Register();
}
Here's the code for SetCompletedOnce this will add a Handler only once
static private void SetCompletedOnce(IBackgroundTaskRegistration reg, BackgroundTaskCompletedEventHandler handler)
{
// Validate
if (reg == null) throw new ArgumentNullException("reg");
if (handler == null) throw new ArgumentNullException("handler");
// Unsubscribe in case already subscribed
reg.Completed -= handler;
// Subscribe
reg.Completed += handler;
}
I have also Written the BackgroundLibrary.RFBackgroundTask.cs
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
Debug.WriteLine(taskInstance.TriggerDetails.GetType());
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
Debug.WriteLine("RFComm Task Running");
Debug.WriteLine(taskInstance.TriggerDetails.GetType().ToString());
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}
The Run Method is Invoked Every Time The Device tries to Open the Connection.
The type of the Trigger that is obtained (the type I am debugging in the run method of the RFBackgroundTask.cs) is printed as
Windows.Devices.Bluetooth.Background.RfcommConnectionTriggerDetails
But I am Unable use that because I dont have this Class in the BackgroundLibrary project.
The Documentation says that this Provides information about the Bluetooth device that caused this trigger to fire.
It has Variables like Socket,RemoteDevice etc.
I think I am Missing something very simple
Can you please help me out .
Once your background task is launched, simply cast the TriggerDetails object to an RfcommConnectionTriggerDetails object:
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
RfcommConnectionTriggerDetails details = (RfcommConnectionTriggerDetails)taskInstance.TriggerDetails;
StreamSocket = details.Socket; // Rfcomm Socket
// Access other properties...
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}
I have been porting an existing J2ME mobile app, that allows users to view archived news videos, to the latest Nokia SDK 2.0 platform for Series 40 full-touch devices.
I am using both the LWUIT and LWUIT4IO technologies for the UI and Network functionalities of the application respectively.
The app has been tested to work on the S40 5th Edition SDK platform emulator. Extending LWUIT4IO's ConnectionRequest class and utilizing LWUIT's XMLParser, the app can successfully send a HTTP request and get the expected response data from a web service that basically returns an XML-formatted type of feed (containing necessary metadata for the video) (Here's the URL of the web service: http://nokiamusic.myxph.com/nokianewsfeed.aspx?format=3gp)
But for some reason, this is not the case when trying to run the app on the latest Nokia SDK 2.0 platform. It throws a java.lang.NullPointerException upon trying to parse (XMLParser.parse()) the InputStream response of the web service. When I trace the Network Traffic Monitor of the emulator of the corresponding Request sent and Response received - 0 bytes were returned as content despite a successful response status 200. Apparently the XMLParser object has nothing to parse in the first place.
I am hoping that you can somehow shed light on this issue or share any related resolutions, or help me further refine the problem.
Posted below is the code of the SegmentService class (a sub-class of LWUIT's ConnectionRequest) that connects to the webservice and processes the XML response:
public class SegmentService extends ConnectionRequest implements ParserCallback {
private Vector segments;
private Video segment;
public SegmentService(String backend) {
String slash = backend.endsWith("/") ? "" : "/";
setPost(false);
setUrl(backend + slash + "nokianewsfeed.aspx");
addArgument("format", "3gp");
}
public void setDateFilter(String date) {
System.out.println(date);
addArgument("date", date);
}
private Video getCurrent() {
if (segment == null) {
segment = new Video();
}
return segment;
}
protected void readResponse(InputStream input) throws IOException {
InputStreamReader i = new InputStreamReader(input, "UTF-8");
XMLParser xmlparser = new XMLParser();
System.out.println("Parsing the xml...");
Element element = xmlparser.parse(i);
System.out.println("Root " + element.getTagName());
int max = element.getNumChildren();
System.out.println("Number of children: " + max);
segments = new Vector();
for (int c = 0; c < max; c++) {
Element e = element.getChildAt(c);
System.out.println("segment " + c);
int len = e.getNumChildren();
System.out.println("Number of children: " + len);
for (int d=0; d<len; d++) {
Element s = e.getChildAt(d);
String property = s.getTagName();
System.out.println("key: " + property);
String value = (s.getNumChildren()>0) ? s.getChildAt(0).getText() : null;
System.out.println("value: " + value);
if (property.equals("title")) {
getCurrent().setTitle(value);
} else if (property.equals("description")) {
getCurrent().setDescription(value);
} else if (property.equals("videourl")) {
getCurrent().setVideoUrl(value);
} else if (property.equals("thumburl")) {
getCurrent().setThumbUrl(value);
} else if (property.equals("adurl")) {
getCurrent().setAdUrl(value);
} else if (property.equals("publishdate")) {
getCurrent().setPublishDate(value);
} else if (property.equals("category")) {
getCurrent().setCategory(value);
} else if (property.equals("weburl")) {
getCurrent().setWebUrl(value);
} else if (property.equals("thumburl2")) {
getCurrent().setThumb210(value);
} else if (property.equals("thumburl4")) {
getCurrent().setThumb40(value);
}
}
if (segment != null) {
segments.addElement(segment);
segment = null;
}
}
fireResponseListener(new NetworkEvent(this, segments));
}
public boolean parsingError(int errorId, String tag, String attribute, String value, String description) {
System.out.println(errorId);
System.out.println(tag);
System.out.println(value);
System.out.println(description);
return true;
}
}
I'm calling a powershell script from C#. The script is pretty small and is "gps;$host.SetShouldExit(9)", which list process, and then send back an exit code to be captured by the PSHost object.
The problem I have is when the pipeline has been stopped and disposed, the output reader PSHost collection still seems to be written to, and is filling up. So when I try and copy it to my own output object, it craps out with a OutOfMemoryException when I try to iterate over it. Sometimes it will except with a Collection was modified message. Here is the code.
private void ProcessAndExecuteBlock(ScriptBlock Block)
{
Collection<PSObject> PSCollection = new Collection<PSObject>();
Collection<Object> PSErrorCollection = new Collection<Object>();
Boolean Error = false;
int ExitCode=0;
//Send for exection.
ExecuteScript(Block.Script);
// Process the waithandles.
while (PExecutor.PLine.PipelineStateInfo.State == PipelineState.Running)
{
// Wait for either error or data waithandle.
switch (WaitHandle.WaitAny(PExecutor.Hand))
{
// Data
case 0:
Collection<PSObject> data = PExecutor.PLine.Output.NonBlockingRead();
if (data.Count > 0)
{
for (int cnt = 0; cnt <= (data.Count-1); cnt++)
{
PSCollection.Add(data[cnt]);
}
}
// Check to see if the pipeline has been closed.
if (PExecutor.PLine.Output.EndOfPipeline)
{
// Bring back the exit code.
ExitCode = RHost.ExitCode;
}
break;
case 1:
Collection<object> Errordata = PExecutor.PLine.Error.NonBlockingRead();
if (Errordata.Count > 0)
{
Error = true;
for (int count = 0; count <= (Errordata.Count - 1); count++)
{
PSErrorCollection.Add(Errordata[count]);
}
}
break;
}
}
PExecutor.Stop();
// Create the Execution Return block
ExecutionResults ER = new ExecutionResults(Block.RuleGuid,Block.SubRuleGuid, Block.MessageIdentfier);
ER.ExitCode = ExitCode;
// Add in the data results.
lock (ReadSync)
{
if (PSCollection.Count > 0)
{
ER.DataAdd(PSCollection);
}
}
// Add in the error data if any.
if (Error)
{
if (PSErrorCollection.Count > 0)
{
ER.ErrorAdd(PSErrorCollection);
}
else
{
ER.InError = true;
}
}
// We have finished, so enque the block back.
EnQueueOutput(ER);
}
and this is the PipelineExecutor class which setups the pipeline for execution.
public class PipelineExecutor
{
private Pipeline pipeline;
private WaitHandle[] Handles;
public Pipeline PLine
{
get { return pipeline; }
}
public WaitHandle[] Hand
{
get { return Handles; }
}
public PipelineExecutor(Runspace runSpace, string command)
{
pipeline = runSpace.CreatePipeline(command);
Handles = new WaitHandle[2];
Handles[0] = pipeline.Output.WaitHandle;
Handles[1] = pipeline.Error.WaitHandle;
}
public void Start()
{
if (pipeline.PipelineStateInfo.State == PipelineState.NotStarted)
{
pipeline.Input.Close();
pipeline.InvokeAsync();
}
}
public void Stop()
{
pipeline.StopAsync();
}
}
An this is the DataAdd method, where the exception arises.
public void DataAdd(Collection<PSObject> Data)
{
foreach (PSObject Ps in Data)
{
Data.Add(Ps);
}
}
I put a for loop around the Data.Add, and the Collection filled up with 600k+ so feels like the gps command is still running, but why. Any ideas.
Thanks in advance.
Found the problem. Named the resultant collection and the iterator the same, so as it was iterating, it was adding to the collection, and back into the iterator, and so forth. Doh!.