How can one synchronize an external application with a DigitalMicrograph script? - events

This question has been inspired by the question 'Call script from command line'.
How can one write a script which acts on 'trigger events' in an application other than DigitalMicrograph?
i.e. some script functionality should be triggered by an external application.

The scripting language does not offer many 'external' interfaces in its current state. It is possible to call out to an external process with the command LaunchExternalProcess and wait for the process to complete, but there is no straight-forward way for an external process to call in, i.e. to start a script-action within DigitalMicrograph.
However, it is possible to work around that issue by using the system's file-system as a message queue. To do this, have a script running in the background which regularly checks if a certain file exists, and have the external application create such a file when it wants to trigger a scripting-action in DigitalMicrograph. The file content - if it is a simple text file - can also be used to transport information between the two applications.
Here is an example script which will wait until the file Trigger.txt appears in the root folder. The check is performed every 10seconds.
class WaitForExternal
{
string triggerFilePath
number taskID
void WaitOnTrigger( object self )
{
if ( DoesFileExist( triggerFilePath ) )
{
Result( GetTime(1) + ": Triggered! Now act..." )
If ( TwoButtonDialog( "Do you want to reset the trigger?", "Reset", "Stop" ) )
{
DeleteFile( triggerFilePath )
}
else
{
RemoveMainThreadTask( taskID )
}
}
else
{
Result( GetTime(1) + ": not yet\n" )
}
}
object Init( object self, string triggerPath, number waitSec )
{
triggerFilePath = triggerPath
taskID = self.AddMainThreadPeriodicTask( "WaitOnTrigger", waitSec )
return self
}
}
// Main script
{
string triggerPath = "C:\\Trigger.txt"
number pollingWait = 10
Alloc(WaitForExternal).Init( triggerPath, pollingWait )
}
Note that the periodic task waits idle in the background without interfering with the CPU, but the actual check is then performed on the main thread.

Related

How can i Print own Error Message on Java Jar CMD

how can I Print adittional information to Command line Console?
Output now is:
C:\Users\admin\Desktop\java>java -jar pdf.jar
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at readDataIn.main(readDataIn.java:31)
Code:
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
String arg = args[0];
fileNameSource = "import/" + arg + ".xml";
fileNameTarget = "export/" + arg + ".pdf";
} catch (Exception e) {
// TODO: handle exception
**System.out.println("Personal-Number is missing");**
e.printStackTrace();
}
How can i give the information out, that the Personal Number ist Missing?
First of all, as a general rule you should check for possible exceptions before they actually occur if that is possible, which in your case it definitely is.
So instead of catching the ArrayIndexOutOfBounds insert an if statement that checks the length of the args array before accessing it.
if(args.length == 0){
// no argument has been provided
// handle error here
}
In terms of how to handle the error, there are many options available and depending of what you want to do either could be a good fit.
IllegalArgumentException
It is a common idiom in Java that whenever a function receives an invalid/ illegal argument to throw an IllegalArgumentException.
if (args.length == 0){
throw new IllegalArgumentException("Personal number is missing");
}
This will print the message that you have provided and the stack trace. However if your application should be a Command Line Interface (CLI) you should not use this kind of error handling.
Print message & exit program
if (args.length == 0){
// notice: "err" instead of "out": print to stderr instead of stdout
System.err.println("Personal number is missing");
// exit program with non-zero exit code as exit code == 0 means everything is fine
System.exit(1);
}
For more information on stdout and stderr see this StackOverflow question.
This is what many CLI applications and e.g. java itself does. When you type java fdsdfsdfs or some similar nonsense as an argument Java will give you an error message and exit with some non-zero return code ("1" in this case).
It is also common that CLI applications print an error message and following some usage information on how to correctly use the application or provide a help command so a user can get more information. This happens for example if you just enter java without any parameters.
So it is really up to you what you want to do.
If you are thinking of implementing a full featured CLI application with more (complex) commands with multiple options etc. you should consider using a CLI library like JCommander or Apache Commons CLI as parsing command line arguments can quickly get ugly. All these common things are already handled there.
Logging
In case your application is some script that will be executed in a non-interactive way logging the error to a file and exiting with a non-zero exit code might also be an option.
PS
Your code looks to me like it should not compile at all as you are not declaring a type for your variables fileNameSource and fileNameTarget.
Use String or var here (assuming you're running > Java 11).
String fileNameSource = "import/" + arg + ".xml";
var fileNameTarget = "export/" + arg + ".pdf";
You might also need to consider that your program name is part of the args array, so you might have more than 0 values in the array and therefore might need to adjust the if statements above.
You may be interested in picocli, which is a modern CLI library for Java and other JVM languages.
Picocli does some basic validation automatically, and results in very compact code that produces user-friendly applications. For example:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
#Command(name = "myapp", mixinStandardHelpOptions = true, version = "1.0",
description = "This command does something useful.")
class MyApp implements Runnable {
#Parameters(description = "File name (without extension) of the file to import and export.")
private String personalNumber;
#Override
public void run() {
String fileNameSource = "import/" + personalNumber + ".xml";
String fileNameTarget = "export/" + personalNumber + ".pdf";
// remaining business logic
}
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp()).execute(args));
}
}
If I run this class without any parameters, the following message is printed to the standard error stream, and the process finished with exit code 2. (Exit codes are customizable.)
Missing required parameter: '<personalNumber>'
Usage: myapp [-hV] <personalNumber>
This command does something useful.
<personalNumber> File name (without extension) of the file to import
and export.
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
The usage help message is created automatically from the descriptions of the command, and the descriptions of its options and positional parameters, but can be further customized.
Note how the mixinStandardHelpOptions = true annotation adds --help and --version options to the command. These options are handled by the library without requiring any further logic in the application.
Picocli comes with an annotation processor that makes it very easy to turn your application into a native image with GraalVM. Native images have faster startup time and lower runtime memory overhead compared to a Java VM.

How to run conditional task in Airflow with previous http operator requested value

I am creating a dag file, with multiple SimpleHttpOperator request. I need to skipped the next task if previous task returned a failed status. Only continue with success status.
Tried with BranchPythonOperator, which inside i will decide which task to run next. But seem it is not working.
sample of request_info will return
{
"data":{
"name":"Allan",
"age":"26",
"gender":"male",
"country":"California"
},
"status":"failed"
}
request_info = SimpleHttpOperator(
task_id='get_info',
endpoint='get/information',
http_conn_id='localhost',
data=({"guest":"1"})
headers={"Content-Type":"application/json"},
xcom_push=True,
dag=dag
)
update_info = SimpleHttpOperator(
task_id='update_info',
endpoint='update/information',
http_conn_id='localhost',
data=("{{ti.xcom_pull(task_ids='request_info')}}")
headers={"Content-Type":"application/json"},
xcom_push=True,
dag=dag
)
skipped_task = DummyOperator(
task_id='skipped',
dag=dag
)
skip_task = BranchPythonOperator(
task_id='skip_task',
python_callable=next_task,
dag=dag
)
def next_task(**kwangs):
status="ti.xcom_pull(task_ids='request_info')"
if status == "success":
return "update_info"
else:
return "skipped_task"
return "skipped_task"
request_info.set_downstream(skip_task)
#need set down stream base on ststus
I expect the flow should be, after getting the info. Identify status, if success, proceed update else proceed skipped.
Generally tasks are supposed to be atomic, which means that they operate independently of one another (besides their order of execution). You can share more complex relations and dependencies by using XCom and airflow trigger rules.

How do I export contents between xml tags based on names in Extendscript for Indesign?

All I'd like to do here is open an InDesign 2018 CC file, pull out text uniquely (here I've chosen to grab content inside XML tag called "Title" from named tag window in the InDesign application side), save it to a txt file, and close the InDesign doc. I'm working in the Extendscript app, using Adobe InDesign CC 2018 (13.064). I just need to push to a txt file only certain named data (textboxes, xmltags, pageitems, etc) the contents based on anything, but via the name of the data holder. But xmltags are the only objects that I can name in the InDesign app apart from layers, and layers won't work for other reasons. So I'm stuck not being able to refer to xml-tagged contents. Please help.
Note:
I get an error with this code saying "Title" isn't defined, and I understand the error, but not sure how to utilize the method XML.toString() without referring to an object that's named inside the InDesign file. So I guess I'm using the wrong method to refer to xml-tagged data already located in a file??
So naturally, I throw out XML.toString() and utilize the commented out code (below) "app.activeDocument.xmlItems.item;" thinking maybe I will get an array of all items that are xml tagged, which is not even specific enough for my goal, but I'm desperate, and I get another newer error regarding the "exportfile" line of code: myArticles.exportFile() is not a function.
My code so far:
app.open(File("C:/Users/Sean/Desktop/New folder/va tech 2.indd"), true);
myArticles = Title.toString();
//THIS ATTEMPT WON'T WORK EITHER AS RPLCMNT FOR LINE ABOVE: myArticles= app.activeDocument.xmlItems.item;
myArticles.exportFile(ExportFormat.textType, new File("/C/Users/Sean/Desktop/New folder/test.txt"), false);
app.documents.everyItem().close(SaveOptions.NO);
var main = function() {
var doc, root, xes, n, nXE, st, xc, strs = [],
f = File ( Folder.desktop+"/contents.txt" );
try {
//NEED TO CHANGE THE URL. Ex: some/url > /Users/user/Desktop/foo.indd
doc = app.open ( File ( "some/url" ) );
}
catch(err){
alert(err.message);
return;
}
if ( !doc ) {
alert("You need an open document" );
return;
}
root = doc.xmlElements[0];
xes = root.evaluateXPathExpression("//Title");
n = xes.length;
while ( n-- ) {
nXE = xes[n];
xc = nXE.xmlContent;
if ( xc instanceof Story ) {
strs.push( xc.contents );
}
}
if ( strs.length ) {
f.open('w');
f.write ( strs.reverse().join("\r") );
f.close();
}
}
var u;
app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );

Using IO::Select on STDIN on Windows

When I run the code below on a Linux system, as expected it outputs Nothing is ready about every two seconds, and also outputs anything entered on to console.
But on Windows, can_read returns instantly with zero items.
use IO::Select;
my $sel = IO::Select->new();
$sel->add(\*STDIN);
while ( 1 ) {
my #ready = $sel->can_read(2);
if ( scalar #ready == 0 ) {
print "Nothing is ready\n";
}
foreach my $fh ( #ready ) {
if ( $fh eq \*STDIN ) {
my $in = <STDIN>;
print "got $in from stdin\n";
}
}
}
It seems that select works only on Windows sockets and not on STDIN. How can I use IO::Select on STDIN on a Windows system?
You cannot, perldoc perlport states:
select Only implemented on sockets. (Win32, VMS)
This is caused by Windows itself implementing select() only for sockets, see https://learn.microsoft.com/de-de/windows/desktop/api/winsock2/nf-winsock2-select.
The Windows equivalent seems to be I/O Completion Ports. But you have to find a way to use them from Perl.
If you really just care about STDIN, you can poll in a loop with Term::ReadKey with a ReadMode of -1 (non-blocking). As the name of the module suggests, this may only work on a tty.

Remote Desktop Connection by making .bat file

I want to connect my pc to another pc by making .bat file. When i run that file it should connect to the other pc. I wrote "mstsc /v:192.168.15.102" command when i execute this file it opens remote desktop window and demands username and password. How can i avoid that window and pass the credential in .bat file.
I found this
cmdkey /generic:TERMSRV/$server /user:$user /pass:$Password
mstsc /v:$Server
from archive (or original)
But I think that's for powershell only and I'm inexperienced with windows.
Along the comments I also saw this one:
For people that were looking for this idea but want to use batch instead I created the following. Save this a mstscup.cmd from notepad (don’t forget to remove the .txt at the end!)
Call it from the command line as follows:
mstscup “servername” “user” “pass”
I didn’t like the idea of leaving the user and pass in the password vault so it clears it after 120 seconds (leaves a command prompt window opened).
Feel free to modify at will!
#echo off
setlocal
:: Check if the user passed at least 1 argument
if “%1%” == “” (
echo Remoted Desktop connection with user and password
echo.
echo Missing arguments. Syntax:
echo %~nx0% “servername” “username” “password”
echo.
echo Jean Morin, v0.1, 2013-02-23
pause
goto :eof
)
:: Next line removes surrounding quotes from server name
set sServer=%~1%
:: Keep the quotes for the username and password (in case spaces exists)
set sUser=%2%
set sPass=%3%
:: Seconds to wait before clearing the newly added password from the vault (see control panel, manage your credentials)
:: You may want to modify this if the server takes longer to connect (WAN). You could add this as a fourth argument.
set sSeconds=120
:: Add a new connection definition method to the vault
cmdkey /generic:TERMSRV/%sServer% /user:%sUser% /pass:%sPass%
:: Connect to the server as a new task
start mstsc /v:%sServer%
:: ping ourselves for x seconds (acts like a pause) then removes the newly added password from the vault
ping -n %sSeconds% 127.0.0.1 >nul:
cmdkey /delete:TERMSRV/%sServer%
Best solution I could find was saving an rdp config.
Relatively much simpler than these options.
So open Remote Desktop Connection through start menu or win+r mstsc.exe.
Then select advanced options, change everything you want, and Save As.
This will create an .rdp file that can be clicked to run or run in command prompt.
:)
Remote Login using java and batch file by double click
Create batch file Remote.bat and write the following code,
#echo off
java Remote DEV
Create a java file Remote.java and write following code,
also change the IP address of your remote computer in code.
import java.awt.MouseInfo;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.concurrent.TimeUnit;
public class Remote
{
public static void main(String args[])
{
try
{
//showPosition();
System.out.println("Remote Desktop for-->"+args[0]);
String IP = "";
if("DEV".equalsIgnoreCase(args[0]))
{
IP = "mstsc /v:10.0.41.101";
}
else if("UAT".equalsIgnoreCase(args[0]))
{
IP = "mstsc /v:10.0.45.43";
}
else if("PRE-PROD".equalsIgnoreCase(args[0]))
{
IP = "mstsc /v:10.0.45.209";
}
Process p = Runtime. getRuntime(). exec(IP);
Robot bot = new Robot();
long mask = InputEvent.MOUSE_EVENT_MASK;
TimeUnit.SECONDS.sleep((long) 2.5);
bot.mouseMove(607, 290);
bot.mousePress((int) mask);
bot.mouseRelease((int) mask);
bot.keyPress(KeyEvent.VK_SHIFT);
bot.keyPress(KeyEvent.VK_Y);
bot.keyRelease(KeyEvent.VK_SHIFT);
bot.keyPress(KeyEvent.VK_E);
bot.keyPress(KeyEvent.VK_S);
bot.keyPress(KeyEvent.VK_B);
bot.keyPress(KeyEvent.VK_A);
bot.keyPress(KeyEvent.VK_N);
bot.keyPress(KeyEvent.VK_K);
bot.keyPress(KeyEvent.VK_1);
bot.mouseMove(765, 508);
bot.mousePress((int) mask);
bot.mouseRelease((int) mask);
}
catch (Exception e)
{
System.out.println("Exception send--->"+e.getMessage());
e.printStackTrace();
}
}
public static void showPosition() throws InterruptedException
{
try
{
while(true == true)
{
TimeUnit.SECONDS.sleep(1/2);
double mouseX = MouseInfo.getPointerInfo().getLocation().getX();
double mouseY = MouseInfo.getPointerInfo().getLocation().getY();
System.out.println("X:" + mouseX);
System.out.println("Y:" + mouseY);
//make sure to import
}
}
catch(Exception e)
{
System.out.println("Excpetion inside showPosition-->"+e.getMessage());
}
}
}
Now save the code and double click on Remote.bat.
it will automatically open your remote computer.
Enjoyyyyyyy

Resources