-
Notifications
You must be signed in to change notification settings - Fork 6
/
canomate.py
1851 lines (1616 loc) · 86.8 KB
/
canomate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python3
#
#############################################################################
#
# canomate.py - Automation of Canon camera operations
# using Canon's Camera Control API (CCAPI) over WiFi
# Copyright (c) 2020, Horshack
#
# This module is licensed under GPL v3: http://www.gnu.org/licenses/gpl-3.0.html
#
#############################################################################
#
from applog import *
from strutil import *
import argparse
import ast
import errno
import json
import math
import os
import pathlib
import platform
import re
import requests
import shutil
import subprocess
import sys
import time
import traceback
#
#############################################################################
#
# constants
#
#############################################################################
#
APP_NAME = "canomate"
APP_VERSION = "0.02"
# default values
DEFAULT_CCAPI_HTTP_PORT = 8080
DEFAULT_CCAPI_TIMEOUT_SECS = 5.0
DEFAULT_CCAPI_DOWNLOAD_TIMEOUT_SECS = 0
DEFAULT_CCAPI_TRANSPORT_ERROR_RETRIES = 2
DEFAULT_CCAPI_CMD_ERROR_RETRIES = 2
DEFAULT_CCAPI_MAX_BUSY_RETRY_SECS = 10
DEFAULT_CCAPI_RETRY_DELAY_SECS = 0
DEFAULT_EXIT_ON_CMD_ERR = "Yes"
# app exit codes
ERRNO_OK = 0
ERRNO_BAD_PYTHON_VER = 1
ERRNO_CANT_OPEN_OP_FILE = 2
ERRNO_CANT_READ_OP_FILE = 3
ERRNO_FILE_EXISTS_USER_SPECIFIED_EXIT = 4
ERRNO_CTRL_C_INTERRUPT = 5
ERRNO_CODE_EXCEPTION = 6
ERRNO_BAD_CMD_LINE_ARG = 100
ERRNO_BAD_CMD_LINE_ARG_VALUE = 101
ERRNO_ERROR_PARSING_AUTOMATION_OP_PARAMS = 102
ERRNO_UNKNOWN_AUTOMATION_OP = 103
ERRNO_MISSING_AUTOMATION_PARAM = 104
ERRNO_RUNEXECUTABLE_OP_MISSING_ARGUMENT_LINE= 105
ERRNO_INVALID_TIME_VALUE_SPECIFIED = 106
ERRNO_CANT_CONVERT_BOOL_VAL = 200
ERRNO_CANT_CONVERT_INT_VAL = 201
ERRNO_CANT_CONVERT_STR_VAL = 202
ERRNO_INVALID_OP_PARAM_CHOICE = 203
ERRNO_UNMATCHED_END_GROUP_OP = 204
ERRNO_UNMATCHED_GROUP_OP = 205
ERRNO_CANT_CONVERT_STR_VAL_TO_TIME = 206
ERRNO_UNKNOWN_OP_PARAM = 207
ERRNO_OP_PARAM_INVALID_FOR_OTHER_PARAMS = 208
ERRNO_CCAPI_TRANSPORT_ERROR = 1100
ERRNO_CCAPI_CMD_ERROR = 1101
ERRNO_HTTP_DOWNLOAD_FAILED = 1102
ERRNO_RUN_EXECUTABLE_LAUNCH_FAILED = 1200
ERRNO_RUN_EXECUTABLE_OUTPUT_COPY_FAILED = 1201
ERRNO_RUN_EXECUTABLE_EXIT_MISMATCH_RET_CODE = 1202
ERRNO_RUN_EXECUTABLE_FAILED = 1203
ERRNO_ASSERT_CAMERA_SETTING_WRONG_VALUE = 1300
#
# class to contain most of our application-wide global vars
#
class GlobalVarsStruct:
def __init__(self):
self.appStartTime = None # time app was started (epoch)
self.isWin32 = None # True if we're running on a Windows platform
self.isOSX = None # True if we're running on OSX (Mac)
self.appDir = None # directory this script is running from
self.appDataDir = None # directory where our internal data/log files go
self.args = None # dictionary of command-line arguments (generated by argparse)
self.mainAutomationGroup = None # first/container group for automation operations
self.noYes = ['no', 'yes'] # boolean conversion to no/yes
self.offOn = ['off', 'on'] # boolean conversion to off/on
#
# Class for spwaning a child app including command-line parameters, with
# the output copied to a caller-specified file
#
class RunExecutable:
#
# creates an array of arguments from a space-delimited sequence of arguments
# in a single-string. handles the case of quoted arguments as well
#
@staticmethod
def createArgListFromArgStr(argStr):
return [x.strip('"') for x in re.split('( |".*?")', argStr) if x.strip()]
#
# executes process, waits for completions, optionally copies stdout/stderr
# @return (fExecuteSuccessful, errno) Tuple
#
@staticmethod
def execute(executableNameIncludingPath, cmdLineAsStr, fWaitForCompletion = True, outputFilename=None, outputFileWriteMethod=None, msgDuringExec=""):
# build list containing all command-line parameters, the first of which is the executable we'll be spanwing
cmdLineAsStr = executableNameIncludingPath + ' ' + cmdLineAsStr
cmdLineArgArray = RunExecutable.createArgListFromArgStr(cmdLineAsStr)
# generate filenames that will hold the stdout and stderr of the spwaned process
stdoutFilename = os.path.join(g.appDataDir, "RunExecutable_StdOut.txt")
stderrFilename = os.path.join(g.appDataDir, "RunExecutable_StdErr.txt")
#
# run executable (wait for completion case)
#
if fWaitForCompletion:
if msgDuringExec:
consoleWriteLine(msgDuringExec)
try:
with open(stdoutFilename, "w") as stdoutFile, open(stderrFilename, "w") as stderrFile:
processRunResult = subprocess.run(cmdLineArgArray, stdout=stdoutFile, stderr=stderrFile)
except Exception as e:
applog_e("RunExecutable: Launch failed - \"{:s}\"".format(str(e)))
return (False, ERRNO_RUN_EXECUTABLE_LAUNCH_FAILED)
if msgDuringExec:
consoleClearLine()
#
# copy stdout and stderr out of process to caller-specified filename
#
if outputFilename:
outputFileOpenModeStr = "w" # assume we'll be overwriting the file
if outputFileWriteMethod == "overwrite_first_time":
# overwrite file if this is the first time we're writing to it during this app session
if os.path.exists(outputFilename):
if os.path.getmtime(outputFilename) >= g.appStartTime:
# file last modification is equal to or newer than our app start time, meaning
# we've already written to it once this session. use append instead
outputFileOpenModeStr = "a"
elif outputFileWriteMethod == "append":
outputFileOpenModeStr = "a"
else: assert outputFileWriteMethod == "overwrite"
try:
with open(outputFilename, outputFileOpenModeStr) as outputFile, open(stdoutFilename, "r") as stdoutFile, open(stderrFilename, "r") as stderrFile:
outputFile.write(stdoutFile.read())
outputFile.write(stderrFile.read())
except Exception as e:
applog_e("RunExecutable: Error copying RunExecutable output to \"{:s}\" {:s}".format(outputFilename, str(e)))
return (False, ERRNO_RUN_EXECUTABLE_OUTPUT_COPY_FAILED)
return (True, processRunResult.returncode)
#
# run executable (don't wait for completion)
#
try:
process = subprocess.Popen(cmdLineArgArray)
except Exception as e:
applog_e("RunExecutable: Launch failed - \"{:s}\"".format(str(e)))
return (False, ERRNO_RUN_EXECUTABLE_LAUNCH_FAILED)
return (True, ERRNO_OK)
#
# base class for automation operations. Each derived class corresponds to a user-specified automation
# op, which is specified on either the command line via --op or inside a file via --opfile
#
class AutomationOp:
#
# static class variables
#
lastDownloadedFiles_FullUrl = None # last downloaded file from camera (URL path)
lastDownloadedFiles_LocalPath = None # last downloaded file from camera (local path file written to)
lastFilesFoundDuringPolling = None # list of files last found when polling for new files on camera
#
# base implementation that handles op parameters common to all operations
#
def __init__(self, paramDict):
#
# process parameters common to all ops
#
if not self.isGroupOp():
# process parameters that are only valid for non-group ops
self.repeatCount = self.getDictValueAsScalar(paramDict, 'repeatcount', default=1)
self.delayAfterSecs = self.getDictValueAsSeconds(paramDict, 'delayafter', 0)
else:
#
# set values on group for same parameters not allowed on group. this allow common
# code to operate on groups and non-group items without the need for conditional
# logic that checks whether or not a given op is a non-group. We also need delete
# the keys for these params so we don't interpret them as invalid in self.unusedParamDictKeys;
# they only exist in the group to support the inheritance into the child ops of the group
#
paramDict.pop('repeatcount', None)
paramDict.pop('delayafter', None)
self.repeatCount = 1
self.delayAfterSecs = 0
retryArgsDict = dict()
retryArgsDict['timeout'] = self.getDictValueAsScalar(paramDict, 'timeout', g.args['timeout'], float)
retryArgsDict['downloadtimeout'] = self.getDictValueAsScalar(paramDict, 'downloadtimeout', g.args['downloadtimeout'], float)
retryArgsDict['transportretries'] = self.getDictValueAsScalar(paramDict, 'transportretries', g.args['transportretries'])
retryArgsDict['cmdretries'] = self.getDictValueAsScalar(paramDict, 'cmdretries', g.args['cmdretries'])
retryArgsDict['retrydelay'] = self.getDictValueAsScalar(paramDict, 'retrydelay', g.args['retrydelay'], float)
retryArgsDict['maxbusyretrytime' ] = self.getDictValueAsScalar(paramDict, 'maxbusyretrytime', g.args['maxbusyretrytime'])
retryArgsDict['exitoncmderr'] = self.getDictValueAsBool(paramDict, 'exitoncmderr', g.args['exitoncmderr'])
self.retryInfo = CCAPI.RetryInfo(retryArgsDict)
#
# the superclass and baseclass have now processed (and removed) all the paramDict key values
# they recognize. if we have any keys (parameters) left over then those represent unsupported
# parameters
#
if len(paramDict) > 0:
self.unusedParamDictKeys = paramDict
else:
self.unusedParamDictKeys = None
def getUnusedParamDictKeys(self):
return self.unusedParamDictKeys
def isGroupOp(self):
return self.getClassSuffix() == 'Group'
def execute(self):
raise AssertionError("No execute() method in AutomationOp base class")
#
# returns this instance's class name. Example: "AutomationOp_Delay"
#
def getClassSuffix(self):
return AutomationOp.getAutomationOpClassSuffix(type(self).__name__)
#
# returns the suffix of the instance's class name. Example: "Delay" for AutomationOp_Delay
#
@staticmethod
def getAutomationOpClassSuffix(fullClassName):
return fullClassName[fullClassName.find('_')+1:]
#
# Converts a string value for key into a boolean
# @param paramDict Dictionary containing keys:values
# @param key Key we are to convert from a string to a boolean
# @param default Default value if the key is not found in the dictionary.
# Specify None if the key must exist - if the key is not found the app exits
# with a message
# @return String converted to a boolean
#
def getDictValueAsBool(self, paramDict, key, default=False, fRequireValue=False):
if key in paramDict:
val = paramDict[key]
del paramDict[key]
if type(val).__name__ == 'bool':
return val
if type(val).__name__ == 'int':
return bool(val)
if type(val).__name__ == 'str':
if val.lower() in ['1', 'true', 't', 'yes', 'y', 'on', 'enabled']:
return True
if val.lower() in ['0', 'false', 'f', 'no', 'n', 'off', 'disabled']:
return False
applog_e("For op '{:s}', unable to convert value '{:s}' for parameter '{:s}' to a boolean".format(self.getClassSuffix(), val, key))
exit(ERRNO_CANT_CONVERT_BOOL_VAL)
if fRequireValue:
applog_e("Missing required parameter '{:s}' for op '{:s}'".format(key, self.getClassSuffix()))
exit(ERRNO_MISSING_AUTOMATION_PARAM)
return default
#
# Converts a string value for key into a scalar value (int or float)
# @param paramDict Dictionary containing keys:values
# @param key Key we are to convert from a string to a scalar
# @param default Default value if the key is not found in the dictionary.
# Specify None if the key must exist - if the key is not found the app exits
# with a message
# @param scalarClass Scalar we are to conver to. Supports int and float
# @return String converted to a scalar
#
def getDictValueAsScalar(self, paramDict, key, default=None, scalarClass=int, fRequiredValue=False):
if key in paramDict:
keyStr = paramDict[key]
del paramDict[key]
try:
val = scalarClass(keyStr)
except ValueError:
applog_e("For op '{:s}', unable to convert value '{:s}' for parameter '{:s}' to a {:s}".format(self.getClassSuffix(), keyStr, key, type(scalarClass).__name__))
exit(ERRNO_CANT_CONVERT_INT_VAL)
return val
if fRequiredValue:
applog_e("Missing required parameter '{:s}' for op '{:s}'".format(key, self.getClassSuffix()))
exit(ERRNO_MISSING_AUTOMATION_PARAM)
return default
#
# Gets a string value from the dictionary, applying a default if none is provided
# @param paramDict Dictionary containing keys:values
# @param key Key holding the desired string value
# @param default Default value if the key is not found in the dictionary.
# Specify None if the key must exist - if the key is not found the app exits
# with a message
# @return String, from either the dictionary if present or the default value passed
#
def getDictValueAsStr(self, paramDict, key, default=None, fRequiredValue=False):
if key in paramDict:
val = paramDict[key]
del paramDict[key]
try:
strVal = str(val)
except ValueError:
applog_e("For op '{:s}', unable to convert value '{:s}' for parameter '{:s}' to a string".format(self.getClassSuffix(), val, key))
exit(ERRNO_CANT_CONVERT_STR_VAL)
return strVal
if fRequiredValue:
applog_e("Missing required parameter '{:s}' for op '{:s}'".format(key, self.getClassSuffix()))
exit(ERRNO_MISSING_AUTOMATION_PARAM)
return default
#
# Gets a string value from the dictionary, matching it to a predefined set of choices
# @param paramDict Dictionary containing keys:values
# @param key Key holding the desired string value
# @param listChoices List containing valid choices. Comparison is case-insensitive
# @param default Default value if the key is not found in the dictionary.
# Specify None if the key must exist - if the key is not found the app exits
# with a message
# @return String, from either the dictionary if present or the default value passed
#
def getDictValueStrChoice(self, paramDict, key, listChoices, default=None, fRequiredValue=False):
if key in paramDict:
val = paramDict[key]
del paramDict[key]
if val.lower() in listChoices:
return val.lower()
applog_e("For op '{:s}', invalid option '{:s}' for parameter '{:s}'. Choices: {}".format(self.getClassSuffix(), val, key, listChoices))
exit(ERRNO_INVALID_OP_PARAM_CHOICE)
if fRequiredValue:
applog_e("Missing required parameter '{:s}' for op '{:s}'".format(key, self.getClassSuffix()))
exit(ERRNO_MISSING_AUTOMATION_PARAM)
return default
#
# Converts a string value to a time in seconds. The string can
# be a simple value (seconds) or a time-code in the form of
# hh:mm:ss or mm:ss.
# @param paramDict Dictionary containing keys:values
# @param key Key holding the desired string value
# @param default Default value if the key is not found in the dictionary.
# Specify None if the key must exist - if the key is not found the app exits
# with a message
# @return Convert time value string into seconds
#
def getDictValueAsSeconds(self, paramDict, key, default=None, fRequiredValue=False):
if key in paramDict:
val = paramDict[key]
del paramDict[key]
try:
seconds = convertTimeStrToSeconds(val, False)
except Exception as e:
applog_e("For op '{:s}', unable to convert time value '{:s}' for parameter '{:s}' to a valid time".format(self.getClassSuffix(), val, key))
exit(ERRNO_INVALID_TIME_VALUE_SPECIFIED)
return seconds
if fRequiredValue:
applog_e("Missing required parameter '{:s}' for op '{:s}'".format(key, self.getClassSuffix()))
exit(ERRNO_MISSING_AUTOMATION_PARAM)
return default
def printGetResponse(self, resp, respKeyVal='value'):
fListAbilities = self.fListAbilities if hasattr(self, 'fListAbilities') else False
if resp['success']:
availableStr = " Available: {}".format(resp['ability']) if fListAbilities else ""
infoStr = "{:s}{:s}".format(resp[respKeyVal], availableStr)
else:
infoStr = "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_Delay(AutomationOp):
def __init__(self, paramDict):
self.delaySecs = self.getDictValueAsSeconds(paramDict, 'delaytime', default=None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
delayWithConsoleMessage(self.delaySecs, 'Delay op')
class AutomationOp_WaitForEnterKeyToContinue(AutomationOp):
def __init__(self, paramDict):
self.fBeep = self.getDictValueAsBool(paramDict, 'beep', default=False)
return super().__init__(paramDict)
def execute(self):
if self.fBeep:
beep()
applog_i_nc("{:s}: Press <Enter> to continue...".format(self.getClassSuffix()))
print("{:s}: Press <Enter> to continue...".format(self.getClassSuffix()), end='')
input() # todo-todo: Flush input before checking. sys.stdin.flush() doesn't work
class AutomationOp_Beep(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
beep()
class AutomationOp_PrintMovieMode(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getMovieMode(self.retryInfo)
self.printGetResponse(resp, respKeyVal='status')
class AutomationOp_PrintMovieQuality(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getMovieQuality(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetMovieQuality(AutomationOp):
def __init__(self, paramDict):
self.movieQualityStr = self.getDictValueAsStr(paramDict, 'moviequality', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.movieQualityStr))
ccapi.setMovieQuality(self.retryInfo, self.movieQualityStr)
class AutomationOp_EnterMovieMode(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
ccapi.setMovieMode(self.retryInfo, True)
class AutomationOp_ExitMovieMode(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
ccapi.setMovieMode(self.retryInfo, False)
class AutomationOp_StartMovieRecord(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
ccapi.startStopMovieRecord(self.retryInfo, True)
class AutomationOp_StopMovieRecord(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
ccapi.startStopMovieRecord(self.retryInfo, False)
class AutomationOp_PrintCameraInfo(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
cameraInfo = ccapi.getCameraInfo(self.retryInfo)
if cameraInfo['success']:
cameraInfoStr = "Model: {:s}, S/N: {:s}, Firmware: {:s}".format(
cameraInfo['productname'], cameraInfo['serialnumber'], cameraInfo['firmwareversion'])
else:
cameraInfoStr = "N/A"
infoStr = cameraInfoStr
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_PrintTemperatureStatus(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getTemperatureStatus(self.retryInfo)
infoStr = resp['status'] if resp['success'] else "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_TakePhoto(AutomationOp):
def __init__(self, paramDict):
self.fPerformAF = self.getDictValueAsBool(paramDict, 'autofocus', default=False)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
ccapi.takeStillPhoto(self.retryInfo, self.fPerformAF)
class AutomationOp_PrintBatteryInfo(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getBatteryInfo(self.retryInfo)
if resp['success']:
infoStr = "Name: {:s}, Kind: {:s}, Level: {:s}, Quality: {:s}".format(
resp['name'], resp['kind'], resp['level'], resp['quality'])
else:
infoStr = "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_PrintShootingSettings(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getShootingSettings(self.retryInfo)
if resp['success']:
infoStr = "Mode: {:s}, Aperture: {:s}, SS: {:s}, ISO: {:s}, EC: {:s}".format(
resp['shootingmodedial']['value'], resp['av']['value'], resp['tv']['value'],
resp['iso']['value'], resp['exposure']['value'])
else:
infoStr = "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_GetInfoByUrl(AutomationOp):
def __init__(self, paramDict):
self.url = self.getDictValueAsStr(paramDict, 'url', default=None, fRequiredValue=True)
self.fullUrl = ccapi.genFullUrl(self.url, verPrefix=None)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.get(self.retryInfo, self.fullUrl)
infoStr = "'{:s}': {}".format(self.url, resp) if resp['success'] else "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_DisconnectWireless(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}".format(self.getClassSuffix()))
return ccapi.disconnectWirelss(self.retryInfo)
class AutomationOp_PrintAPI(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getApiInfo(self.retryInfo)
infoStr = "{}".format(resp) if resp['success'] else "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_DownloadFileByUrl(AutomationOp):
def __init__(self, paramDict):
self.url = self.getDictValueAsStr(paramDict, 'url', default=None, fRequiredValue=True)
self.fullUrl = ccapi.genFullUrl(self.url)
self.outputDir = self.getDictValueAsStr(paramDict, 'outputdir', g.args['outputdir'])
self.ifExists = self.getDictValueAsStr(paramDict, 'ifexists', g.args['ifexists'])
return super().__init__(paramDict)
def execute(self):
AutomationOp.lastDownloadedFiles_FullUrl = None
AutomationOp.lastDownloadedFiles_LocalPath = None
#
# formualte the full local path+filename to hold the file and handle
# the case of what to do if the local file already exists.
#
filenameFromUrl = CCAPI.getFilenameFromUrl(self.fullUrl)
localFileFullPath = os.path.join(self.outputDir, filenameFromUrl)
localFileFullPath = checkWritingNewFileExists(localFileFullPath, self.ifExists)
if localFileFullPath:
#
# file didn't exist or we're configured to generate a unique name. in either case,
# 'localFileFullPath' holds the local path+filename we're to write the file to
#
(fFailed, downloadTimeSecs) = ccapi.downloadFile(self.retryInfo, self.fullUrl, localFileFullPath)
if fFailed == False:
AutomationOp.lastDownloadedFiles_FullUrl = [self.fullUrl]
AutomationOp.lastDownloadedFiles_LocalPath = [localFileFullPath]
applog_i("{:s} From {:s} stored at {:s} [download time = {:.2f} (s)]".format(self.getClassSuffix(), self.url, localFileFullPath, downloadTimeSecs))
class AutomationOp_GetPendingEvents(AutomationOp):
def __init__(self, paramDict):
self.fPrintEvents = self.getDictValueAsBool(paramDict, 'printevents', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getPolledUpdate(self.retryInfo)
if self.fPrintEvents:
infoStr = "{}".format(resp) if resp['success'] else "N/A"
else:
infoStr = "Cleared-only (not printed)" if resp['success'] else "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_WaitForNewFilesOnCamera(AutomationOp):
def __init__(self, paramDict):
self.maxWaitSecs = self.getDictValueAsScalar(paramDict, 'maxwaittime', default=None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
AutomationOp.lastFilesFoundDuringPolling = None
newFileList = ccapi.pollForNewFilesOnCamera(self.retryInfo, self.maxWaitSecs)
applog_i("{:s}: {}".format(self.getClassSuffix(), newFileList))
AutomationOp.lastFilesFoundDuringPolling = newFileList
class AutomationOp_DownloadNewFilesPolled(AutomationOp):
def __init__(self, paramDict):
self.outputDir = self.getDictValueAsStr(paramDict, 'outputdir', g.args['outputdir'])
self.ifExists = self.getDictValueAsStr(paramDict, 'ifexists', g.args['ifexists'])
return super().__init__(paramDict)
def execute(self):
AutomationOp.lastDownloadedFiles_FullUrl = []
AutomationOp.lastDownloadedFiles_LocalPath = []
if AutomationOp.lastFilesFoundDuringPolling == None:
applog_i("{:s}: No files from previous WaitForNewFilesOnCamera".format(self.getClassSuffix()))
return
applog_i("{:s}: File(s):".format(self.getClassSuffix()))
for index, fullUrl in enumerate(AutomationOp.lastFilesFoundDuringPolling):
#
# formualte the full local path+filename to hold the file and handle
# the case of what to do if the local file already exists.
#
filenameFromUrl = CCAPI.getFilenameFromUrl(fullUrl)
localFileFullPath = os.path.join(self.outputDir, filenameFromUrl)
localFileFullPath = checkWritingNewFileExists(localFileFullPath, self.ifExists)
if localFileFullPath:
#
# file didn't exist or we're configured to generate a unique name. in either case,
# 'localFileFullPath' holds the local path+filename we're to write the file to
#
(fFailed, downloadTimeSecs) = ccapi.downloadFile(self.retryInfo, fullUrl, localFileFullPath)
if fFailed == False:
AutomationOp.lastDownloadedFiles_FullUrl.append(fullUrl)
AutomationOp.lastDownloadedFiles_LocalPath.append(localFileFullPath)
applog_i("{:s}: {:s} stored at {:s} [download time = {:.2f} (s)]".format(self.getClassSuffix(), filenameFromUrl, localFileFullPath, downloadTimeSecs))
if len(AutomationOp.lastDownloadedFiles_FullUrl) == 0:
# if no files were downloaded then reset our last-downloaded vars to None (instead of an empty list)
AutomationOp.lastDownloadedFiles_FullUrl = None
AutomationOp.lastDownloadedFiles_LocalPath = None
class AutomationOp_GetInfoOnNewFilesPolled(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
if AutomationOp.lastFilesFoundDuringPolling == None:
applog_i("{:s}: No files from previous WaitForNewFilesOnCamera".format(self.getClassSuffix()))
return
applog_i("{:s}: File(s):".format(self.getClassSuffix()))
for index, fullUrl in enumerate(AutomationOp.lastFilesFoundDuringPolling):
resp = ccapi.getFileInfo(self.retryInfo, fullUrl)
if resp['success']:
infoStr = "Size: {:s}, Duration: {:s}, Date: {:s}".format(getHumanReadableSize(int(resp['filesize'])),
str(resp['playtime']) + ' (s)' if type(resp['playtime']).__name__ == 'int' else 'N/A',
resp['lastmodifieddate'])
else:
infoStr = "N/A"
applog_i("File #{:d}: Name: {:s} {:s}".format(index+1, CCAPI.getFilenameFromUrl(fullUrl), infoStr))
class AutomationOp_RunExecutable(AutomationOp):
def __init__(self, paramDict):
self.executable = self.getDictValueAsStr(paramDict, 'executable', None, fRequiredValue=True)
self.args = self.getDictValueAsStr(paramDict, 'args', None, fRequiredValue=True) # required, even if blank
self.fAppendLastDownloadedFilenames = self.getDictValueAsBool(paramDict, 'appendlastdownloadstoargs', False)
self.fExitOnLaunchErr = self.getDictValueAsBool(paramDict, 'exitonlauncherr', True)
self.fWaitForCompletion = self.getDictValueAsBool(paramDict, 'waitforcompletion', True)
# following options are only valid when waiting for completion. we still call getDictXXX since we pass default values for them to RunExecutable.execute()
if not self.fWaitForCompletion:
if 'outputfile' in paramDict or 'writemode' in paramDict or 'assertexitcode' in paramDict:
applog_i("{:s}: outputfile/writemode/assertexitcode not valid when 'waitforcompletion' is false".format(self.getClassSuffix()))
# don't exit, so that the line # of the above can be reported before we exit due to unusedparams
return super().__init__(paramDict)
self.outputFilename = self.getDictValueAsStr(paramDict, 'outputfile', "")
self.outputFileWriteMethod = self.getDictValueStrChoice(paramDict, 'writemode', ['append', 'overwrite', 'overwrite_first_time'], 'append')
self.assertExitCode = self.getDictValueAsScalar(paramDict, 'assertexitcode', None, int, fRequiredValue=False)
return super().__init__(paramDict)
def execute(self):
args = self.args
if self.fAppendLastDownloadedFilenames and AutomationOp.lastDownloadedFiles_LocalPath != None:
filenameArgsList = ['"{:s}"'.format(x) for x in AutomationOp.lastDownloadedFiles_LocalPath] # build list with each filename in quotes (in case there are spaces in filename)
filenameArgsStr = " ".join(filenameArgsList) # convert list to a single string, with a space between each quoted filename
args += ' ' + filenameArgsStr
applog_i("{:s}: exec={:s} args={:s} [{:s}]".format(self.getClassSuffix(), self.executable, args, ['NO_WAIT', 'WAIT'][self.fWaitForCompletion]))
(fExecuteSuccessful, _errno) = RunExecutable.execute(self.executable, args, self.fWaitForCompletion, self.outputFilename, self.outputFileWriteMethod, "Running: {:s}".format(self.executable))
if self.fExitOnLaunchErr and fExecuteSuccessful == False:
exit(_errno)
if self.fWaitForCompletion:
if self.assertExitCode != None and _errno != self.assertExitCode:
applog_i("{:s}: Terminating due exit code of {:d} not matching 'assertExitCode' value of {:d}".format(self.getClassSuffix(), _errno, self.assertExitCode))
exit(ERRNO_RUN_EXECUTABLE_EXIT_MISMATCH_RET_CODE)
class AutomationOp_PrintMessageToLog(AutomationOp):
def __init__(self, paramDict):
self.message = self.getDictValueAsStr(paramDict, 'message', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.message))
class AutomationOp_ExitApp(AutomationOp):
def __init__(self, paramDict):
self.exitCode = self.getDictValueAsScalar(paramDict, 'exitcode', 0, int)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: exitcode={:d}".format(self.getClassSuffix(), self.exitCode))
exit(self.exitCode)
class AutomationOp_PrintAperture(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getAperture(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetAperture(AutomationOp):
def __init__(self, paramDict):
self.apertureStr = self.getDictValueAsStr(paramDict, 'aperture', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.apertureStr))
ccapi.setAperture(self.retryInfo, self.apertureStr)
class AutomationOp_PrintShutterSpeed(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getShutterSpeed(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetShutterSpeed(AutomationOp):
def __init__(self, paramDict):
self.shutterSpeedStr = self.getDictValueAsStr(paramDict, 'shutterspeed', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.shutterSpeedStr))
ccapi.setShutterSpeed(self.retryInfo, self.shutterSpeedStr)
class AutomationOp_PrintIso(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getIso(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetIso(AutomationOp):
def __init__(self, paramDict):
self.isoStr = self.getDictValueAsStr(paramDict, 'iso', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.isoStr))
ccapi.setIso(self.retryInfo, self.isoStr)
class AutomationOp_PrintExposureCompensation(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getExposureCompensation(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetExposureCompensation(AutomationOp):
def __init__(self, paramDict):
self.exposureCompensationStr = self.getDictValueAsStr(paramDict, 'exposurecompensation', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.exposureCompensationStr))
ccapi.setExposureCompensation(self.retryInfo, self.exposureCompensationStr)
class AutomationOp_PrintWhiteBalance(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getWhiteBalance(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetWhiteBalance(AutomationOp):
def __init__(self, paramDict):
self.whiteBalanceStr = self.getDictValueAsStr(paramDict, 'whitebalance', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.whiteBalanceStr))
ccapi.setWhiteBalance(self.retryInfo, self.whiteBalanceStr)
class AutomationOp_PrintShootingModeDial(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getShootingModeDial(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_PrintDriveMode(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getDriveMode(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetDriveMode(AutomationOp):
def __init__(self, paramDict):
self.driveModeStr = self.getDictValueAsStr(paramDict, 'drivemode', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.driveModeStr))
ccapi.setDriveMode(self.retryInfo, self.driveModeStr)
class AutomationOp_PrintAfOperation(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getAfOperation(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_PrintAfMethod(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getAfMethod(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetAfMethod(AutomationOp):
def __init__(self, paramDict):
self.afMethodStr = self.getDictValueAsStr(paramDict, 'afmethod', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.afMethodStr))
ccapi.setAfMethod(self.retryInfo, self.afMethodStr)
class AutomationOp_PrintMeteringMode(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getMeteringMode(self.retryInfo)
self.printGetResponse(resp)
class AutomationOp_SetMeteringMode(AutomationOp):
def __init__(self, paramDict):
self.meteringModeStr = self.getDictValueAsStr(paramDict, 'meteringmode', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: {:s}".format(self.getClassSuffix(), self.meteringModeStr))
ccapi.setMeteringMode(self.retryInfo, self.meteringModeStr)
class AutomationOp_PrintStillImageQuality(AutomationOp):
def __init__(self, paramDict):
self.fListAbilities = self.getDictValueAsBool(paramDict, 'listavailable', default=False)
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getStillImageQuality(self.retryInfo)
if resp['success']:
currentStr = "Raw: {:s}, JPEG: {:s}".format(resp['value']['raw'], resp['value']['jpeg'])
if self.fListAbilities:
availableStr = " Available: Raw: {}, JPEG: {}".format(resp['ability']['raw'], resp['ability']['jpeg'])
else:
availableStr = ""
infoStr = "{:s}{:s}".format(currentStr, availableStr)
print(resp['value']['jpeg'])
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_SetStillImageQuality(AutomationOp):
def __init__(self, paramDict):
self.rawQualityStr = self.getDictValueAsStr(paramDict, 'rawquality', None, fRequiredValue=True)
self.jpegQualityStr = self.getDictValueAsStr(paramDict, 'jpegquality', None, fRequiredValue=True)
return super().__init__(paramDict)
def execute(self):
applog_i("{:s}: Raw: {:s}, JPEG: {:s}".format(self.getClassSuffix(), self.rawQualityStr, self.jpegQualityStr))
ccapi.setStillImageQuality(self.retryInfo, self.rawQualityStr, self.jpegQualityStr)
class AutomationOp_PrintLensInfo(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getLensInfo(self.retryInfo)
if resp['success']:
infoStr = "{:s}".format(resp['name'] if resp['mount'] else "No lens mounted")
else:
infoStr = "N/A"
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_AssertCameraSettings(AutomationOp):
def __init__(self, paramDict):
self.modeDialSetting = self.getDictValueAsStr(paramDict, 'modedial', default=None)
self.fInMovieMode = self.getDictValueAsBool(paramDict, 'moviemode', default=None)
self.afOperation = self.getDictValueAsStr(paramDict, 'afoperation', default=None)
return super().__init__(paramDict)
def execute(self):
if self.modeDialSetting != None:
resp = ccapi.getShootingModeDial(self.retryInfo)
if resp['success'] and resp['value'] != self.modeDialSetting:
applog_i("{:s}: Mode dial setting is \"{:s}\" but must be \"{:s}\"".format(self.getClassSuffix(), resp['value'], self.modeDialSetting))
exit(ERRNO_ASSERT_CAMERA_SETTING_WRONG_VALUE)
if self.fInMovieMode != None:
resp = ccapi.getMovieMode(self.retryInfo)
if resp['success']:
fInMovieMode = resp['status'] == 'on'
if fInMovieMode != self.fInMovieMode:
applog_i("{:s}: Movie Mode is \"{:s}\" but must be \"{:s}\"".format(self.getClassSuffix(), g.offOn[fInMovieMode], g.offOn[self.fInMovieMode]) )
exit(ERRNO_ASSERT_CAMERA_SETTING_WRONG_VALUE)
if self.afOperation != None:
resp = ccapi.getAfOperation(self.retryInfo)
if resp['success'] and resp['value'] != self.afOperation:
applog_i("{:s}: AF operation is \"{:s}\" but must be \"{:s}\"".format(self.getClassSuffix(), resp['value'], self.afOperation))
exit(ERRNO_ASSERT_CAMERA_SETTING_WRONG_VALUE)
applog_i("{:s}: Conditions met".format(self.getClassSuffix()))
class AutomationOp_PrintCameraDateTime(AutomationOp):
def __init__(self, paramDict):
return super().__init__(paramDict)
def execute(self):
resp = ccapi.getDateTime(self.retryInfo)
infoStr = CCAPI.convertCameraDateTimeResp(resp)
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_SyncDateTime(AutomationOp):
def __init__(self, paramDict):
self.skewSecs = self.getDictValueAsScalar(paramDict, 'skewsecs', 0.7, float, fRequiredValue=False)
return super().__init__(paramDict)
def execute(self):
# get camera's current date/time so we can later print the before/after camera time
resp = ccapi.getDateTime(self.retryInfo)
cameraCurrentDateTimeStr = CCAPI.convertCameraDateTimeResp(resp)
# set the camera's date/time to this system's date time
dateTimeStr = CCAPI.genCameraDateTimeStr(time.time() + self.skewSecs)
ccapi.setDateTime(self.retryInfo, dateTimeStr, True if time.daylight else False)
# print out what we've done
cameraNewDateTimeStr = "{:s}{:s}".format(dateTimeStr, " DST" if time.daylight else "")
infoStr = "Changed camera time from \"{:s}\" to \"{:s}\"".format(cameraCurrentDateTimeStr, cameraNewDateTimeStr)
applog_i("{:s}: {:s}".format(self.getClassSuffix(), infoStr))
class AutomationOp_Group(AutomationOp):
def __init__(self, paramDict):
self.groupName = self.getDictValueAsStr(paramDict, 'groupname', default="")
self.groupRepeatCount = self.getDictValueAsScalar(paramDict, 'grouprepeatcount', default=1)
self.ops = []
return super().__init__(paramDict)
def appendOp(self, op):
self.ops.append(op)
def execute(self):
for groupIteration in range(self.groupRepeatCount):
if self.groupName != '.MainGroup': # don't print out banner for main group - it's an internal construct to contain the entire automation script
applog_i("Performing group \"{:s}\", Iteration {:d}/{:d}".format(self.groupName, groupIteration+1, self.groupRepeatCount))
for opIndex, op in enumerate(self.ops):
repeatCount = op.repeatCount
for opIteration in range(repeatCount):
op.execute()
if op.delayAfterSecs > 0:
delayWithConsoleMessage(op.delayAfterSecs, "'delayafter' OP param")
#
# list of all automation operation classes. Update this list whenever adding a new class. It is used to create
# instances based on the ops specified by the user on the command line or in the ops file
#
AutomationOpClasses = [
AutomationOp_Group, AutomationOp_Delay, AutomationOp_PrintMovieQuality, AutomationOp_SetMovieQuality,
AutomationOp_EnterMovieMode, AutomationOp_ExitMovieMode, AutomationOp_StartMovieRecord, AutomationOp_StopMovieRecord,
AutomationOp_PrintCameraInfo, AutomationOp_PrintTemperatureStatus,
AutomationOp_TakePhoto, AutomationOp_PrintBatteryInfo, AutomationOp_PrintShootingSettings, AutomationOp_GetInfoByUrl,
AutomationOp_DisconnectWireless, AutomationOp_PrintAPI, AutomationOp_DownloadFileByUrl,
AutomationOp_WaitForNewFilesOnCamera, AutomationOp_GetInfoOnNewFilesPolled, AutomationOp_RunExecutable,
AutomationOp_DownloadNewFilesPolled, AutomationOp_PrintMessageToLog, AutomationOp_ExitApp,
AutomationOp_PrintAperture, AutomationOp_SetAperture, AutomationOp_PrintShutterSpeed, AutomationOp_SetShutterSpeed,
AutomationOp_PrintIso, AutomationOp_SetIso, AutomationOp_PrintExposureCompensation, AutomationOp_SetExposureCompensation,
AutomationOp_PrintWhiteBalance, AutomationOp_SetWhiteBalance, AutomationOp_PrintShootingModeDial,
AutomationOp_PrintDriveMode, AutomationOp_SetDriveMode, AutomationOp_PrintAfMethod, AutomationOp_SetAfMethod,
AutomationOp_PrintMeteringMode, AutomationOp_SetMeteringMode, AutomationOp_PrintStillImageQuality, AutomationOp_SetStillImageQuality,
AutomationOp_PrintLensInfo, AutomationOp_WaitForEnterKeyToContinue, AutomationOp_Beep, AutomationOp_AssertCameraSettings,
AutomationOp_PrintMovieMode, AutomationOp_PrintAfOperation, AutomationOp_GetPendingEvents, AutomationOp_SyncDateTime,
AutomationOp_PrintCameraDateTime
]
#
# Canon CCAPI access class
#
class CCAPI:
class RetryInfo:
def __init__(self, retryArgsDict):
# timeouts/retries allowed
self.timeoutSecs = retryArgsDict['timeout']
self.downloadTimeoutSecs = retryArgsDict['downloadtimeout']
self.transportErrorRetriesAllowed = retryArgsDict['transportretries']
self.cmdErrorRetriesAllowed = retryArgsDict['cmdretries']
self.retryDelaySecs = retryArgsDict['retrydelay']
self.maxBusyTimeRetrySecs = retryArgsDict['maxbusyretrytime']
self.fExitOnCmdErr = retryArgsDict['exitoncmderr']
def cmdStarting(self):