Compare commits

...

12 Commits

@ -16,34 +16,6 @@ import shepherd.plugin
#Start new thread, and push ID and core config to api.shepherd.distreon.net/client/update #Start new thread, and push ID and core config to api.shepherd.distreon.net/client/update
class UpdateManager():
def __init__(self):
pass
class SequenceUpdate():
Item = namedtuple('Item', ['sequence_number', 'content'])
def __init__(self):
self.items = []
self._sequence_count = 0
self._dirty = False
def _next_sequence_number(self):
# TODO: need to establish a max sequence number, so that it can be compared to half
# that range and wrap around.
self._sequence_count +=1
return self._sequence_count
def mark_as_dirty(self):
self._dirty = True
def add_item(self, item):
self.items.append(self.Item(self._next_sequence_number(), item))
self.mark_as_dirty()
def get_payload():
pass
def process_ack():
pass
client_id = None client_id = None
control_url = None control_url = None

@ -171,7 +171,6 @@ def compile_config(default_config_path):
@click.group(invoke_without_command=True) @click.group(invoke_without_command=True)
#help="Path to default config TOML file" #help="Path to default config TOML file"
@click.argument('default_config', default="shepherd-default.toml", type=click.Path()) @click.argument('default_config', default="shepherd-default.toml", type=click.Path())
@click.pass_context @click.pass_context
def cli(ctx, default_config): def cli(ctx, default_config):
@ -191,8 +190,8 @@ def cli(ctx, default_config):
core_conf, plugin_classes, plugin_configs = compile_config(default_config) core_conf, plugin_classes, plugin_configs = compile_config(default_config)
if args.test is None: # if args.test is None:
control.init_control(core_conf, plugin_configs) # control.init_control(core_conf, plugin_configs)
scheduler.init_scheduler(core_conf) scheduler.init_scheduler(core_conf)
plugin.init_plugins(plugin_classes, plugin_configs, core_conf) plugin.init_plugins(plugin_classes, plugin_configs, core_conf)
@ -209,11 +208,11 @@ def cli(ctx, default_config):
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
pass pass
@click.argument('plugin_function') @click.argument('plugin_function')
@cli.command() @cli.command()
def test(): def test(plugin_function):
if args.test is not None: (test_plugin, test_func) = plugin_function.split(':')
(test_plugin, test_func) = args.test.split(':') func = getattr(plugin.plugin_functions[test_plugin], test_func)
func = getattr(shepherd.plugin.plugin_functions[test_plugin], test_func)
print(func()) print(func())
return return

@ -1,77 +1,48 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import shepherd.config import shepherd.config as shconf
import shepherd.module import shepherd.plugin
import sys import sys
import os import os
import time import time
import argparse import argparse
from gpiozero import OutputDevice, Device
from gpiozero.pins.pigpio import PiGPIOFactory
from shepherd.modules.betterservo import BetterServo class AphidtrapModule(shepherd.plugin.Plugin):
@staticmethod
def define_config(confdef):
pass
Device.pin_factory = PiGPIOFactory() def __init__(self, pluginInterface, config):
super().__init__(pluginInterface, config)
self.config = config
APHIDTRAP_LED_PIN = 5 #Out2 self.interface = pluginInterface
self.plugins = pluginInterface.other_plugins
self.hooks = pluginInterface.hooks
class AphidtrapConfDef(shepherd.config.ConfDefinition):
def __init__(self):
super().__init__()
class AphidtrapModule(shepherd.module.SimpleModule):
conf_def = AphidtrapConfDef()
def setup(self):
print("Aphidtrap config:") print("Aphidtrap config:")
print(self.config) print(self.config)
self.led_power = OutputDevice(APHIDTRAP_LED_PIN, if "picam" in self.plugins:
active_high=True, self.interface.attach_hook("picam", "pre_cam", self.led_on)
initial_value=False) self.interface.attach_hook("picam", "post_cam", self.led_off)
elif "usbcam" in self.plugins:
self.interface.attach_hook("usbcam", "pre_cam", self.led_on)
self.interface.attach_hook("usbcam", "post_cam", self.led_off)
else:
raise ValueError("Need to either have picam or usbcam plugin loaded")
def setup_other_modules(self): self.interface.register_function(self.test)
self.modules.picam.hook_pre_cam.attach(self.led_on)
self.modules.picam.hook_post_cam.attach(self.led_off)
def led_on(self): def led_on(self):
self.led_power.on() self.plugins["scout"].set_out2(True)
def led_off(self): def led_off(self):
self.led_power.off() self.plugins["scout"].set_out2(False)
def main(argv): def test(self):
argparser = argparse.ArgumentParser( self.led_on()
description='Module for aphidtrap control functions. Run for testing')
argparser.add_argument("configfile", nargs='?', metavar="configfile",
help="Path to configfile", default="conf.toml")
args = argparser.parse_args()
confman = shepherd.config.ConfigManager()
srcdict = {"aphidtrap": {}}
if os.path.isfile(args.configfile):
confman.load(args.configfile)
else:
confman.load(srcdict)
aphidtrap_mod = AphidtrapModule(confman.get_config("aphidtrap", AphidtrapConfDef()),
shepherd.module.Interface(None))
aphidtrap_mod.led_on()
time.sleep(2) time.sleep(2)
aphidtrap_mod.led_off() self.led_off()
if __name__ == "__main__":
main(sys.argv[1:])

@ -25,7 +25,7 @@ class FlytrapPlugin(shepherd.plugin.Plugin):
self.hooks = pluginInterface.hooks self.hooks = pluginInterface.hooks
self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"]) self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"])
self.id = pluginInterface.coreconfig["id"] self.id = pluginInterface.coreconfig["name"]
print("Flytrap config:") print("Flytrap config:")
print(self.config) print(self.config)

@ -9,7 +9,6 @@ import time
import argparse import argparse
class MothtrapPlugin(shepherd.plugin.Plugin): class MothtrapPlugin(shepherd.plugin.Plugin):
@staticmethod @staticmethod
def define_config(confdef): def define_config(confdef):
@ -25,26 +24,29 @@ class MothtrapPlugin(shepherd.plugin.Plugin):
self.hooks = pluginInterface.hooks self.hooks = pluginInterface.hooks
self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"]) self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"])
self.id = pluginInterface.coreconfig["id"] self.id = pluginInterface.coreconfig["name"]
print("Mothtrap config:") print("Mothtrap config:")
print(self.config) print(self.config)
#servo_max = self.config["servo_open_pulse"] / 1000000 #servo_max = self.config["servo_open_pulse"] / 1000000
#servo_min = self.config["servo_closed_pulse"] / 1000000 #servo_min = self.config["servo_closed_pulse"] / 1000000
# if servo_min > servo_max: # if servo_min > servo_max:
# servo_min, servo_max = servo_max, servo_min # servo_min, servo_max = servo_max, servo_min
#print(F"Supplied min: {servo_min}, max: {servo_max}") #print(F"Supplied min: {servo_min}, max: {servo_max}")
if "picam" in self.plugins:
self.interface.attach_hook("picam", "pre_cam", self.led_on)
self.interface.attach_hook("picam", "post_cam", self.led_off)
self.interface.attach_hook("picam", "post_cam", self.run_servo)
elif "usbcam" in self.plugins:
self.interface.attach_hook("usbcam", "pre_cam", self.led_on) self.interface.attach_hook("usbcam", "pre_cam", self.led_on)
self.interface.attach_hook("usbcam", "post_cam", self.led_off) self.interface.attach_hook("usbcam", "post_cam", self.led_off)
self.interface.attach_hook("usbcam", "post_cam", self.run_servo) self.interface.attach_hook("usbcam", "post_cam", self.run_servo)
else:
raise ValueError("Need to either have picam or usbcam plugin loaded")
self.interface.register_function(self.test) self.interface.register_function(self.test)

@ -43,7 +43,7 @@ class PiCamPlugin(shepherd.plugin.Plugin):
confdef.add_def('jpeg_quality', shconf.IntDef(default=80, minval=60, maxval=95, optional=True, confdef.add_def('jpeg_quality', shconf.IntDef(default=80, minval=60, maxval=95, optional=True,
helptext="JPEG quality to save with. Max of 95, passed directly to Pillow")) helptext="JPEG quality to save with. Max of 95, passed directly to Pillow"))
array = confdef.add_def('trigger', shconf.TableArrayDef( array = confdef.add_def('trigger', shconf.DictListDef(
helptext="Array of triggers that will use all cameras")) helptext="Array of triggers that will use all cameras"))
array.add_def('hour', shconf.StringDef()) array.add_def('hour', shconf.StringDef())
array.add_def('minute', shconf.StringDef()) array.add_def('minute', shconf.StringDef())
@ -57,7 +57,7 @@ class PiCamPlugin(shepherd.plugin.Plugin):
self.hooks = pluginInterface.hooks self.hooks = pluginInterface.hooks
self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"]) self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"])
self.id = pluginInterface.coreconfig["id"] self.id = pluginInterface.coreconfig["name"]
self.interface.register_hook("pre_cam") self.interface.register_hook("pre_cam")
self.interface.register_hook("post_cam") self.interface.register_hook("post_cam")
@ -70,7 +70,7 @@ class PiCamPlugin(shepherd.plugin.Plugin):
# Seconds to wait for exposure and white balance auto-adjust to stabilise # Seconds to wait for exposure and white balance auto-adjust to stabilise
self.stabilise_delay = 3 self.stabilise_delay = 3
if self.config["save_directory"] is "": if self.config["save_directory"] == "":
self.save_directory = os.path.join(self.root_dir, "picamera") self.save_directory = os.path.join(self.root_dir, "picamera")
else: else:
self.save_directory = self.config["save_directory"] self.save_directory = self.config["save_directory"]
@ -130,7 +130,6 @@ class PiCamPlugin(shepherd.plugin.Plugin):
overlay.putalpha(128) overlay.putalpha(128)
return overlay return overlay
def camera_job(self): def camera_job(self):
self.hooks.pre_cam() self.hooks.pre_cam()

@ -161,7 +161,7 @@ class Bucket():
class UploaderPlugin(shepherd.plugin.Plugin): class UploaderPlugin(shepherd.plugin.Plugin):
@staticmethod @staticmethod
def define_config(confdef): def define_config(confdef):
dests = confdef.add_def('destination', shconf.TableArrayDef()) dests = confdef.add_def('destination', shconf.DictListDef())
dests.add_def('name', shconf.StringDef()) dests.add_def('name', shconf.StringDef())
dests.add_def('protocol', shconf.StringDef()) dests.add_def('protocol', shconf.StringDef())
dests.add_def('address', shconf.StringDef(optional=True)) dests.add_def('address', shconf.StringDef(optional=True))
@ -174,7 +174,7 @@ class UploaderPlugin(shepherd.plugin.Plugin):
dests.add_def('add_id_to_path', shconf.BoolDef( dests.add_def('add_id_to_path', shconf.BoolDef(
default=True, optional=True)) default=True, optional=True))
buckets = confdef.add_def('bucket', shconf.TableArrayDef()) buckets = confdef.add_def('bucket', shconf.DictListDef())
buckets.add_def('name', shconf.StringDef()) buckets.add_def('name', shconf.StringDef())
buckets.add_def('open_link_on_new', shconf.BoolDef()) buckets.add_def('open_link_on_new', shconf.BoolDef())
buckets.add_def('opportunistic', shconf.BoolDef( buckets.add_def('opportunistic', shconf.BoolDef(
@ -190,7 +190,7 @@ class UploaderPlugin(shepherd.plugin.Plugin):
self.hooks = pluginInterface.hooks self.hooks = pluginInterface.hooks
self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"]) self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"])
self.id = pluginInterface.coreconfig["id"] self.id = pluginInterface.coreconfig["name"]
print("Uploader config:") print("Uploader config:")
print(self.config) print(self.config)

@ -29,11 +29,15 @@ CameraPort = namedtuple(
'CameraPort', ['usbPath', 'devicePath']) 'CameraPort', ['usbPath', 'devicePath'])
# Short wrapper to allow use in a ``with`` context # Short wrapper to allow use in a ``with`` context
class VideoCaptureCtx(): class VideoCaptureCtx():
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.capture_dev = cv2.VideoCapture(*args, **kwargs) self.capture_dev = cv2.VideoCapture(*args, **kwargs)
def __enter__(self): def __enter__(self):
return self.capture_dev return self.capture_dev
def __exit__(self, *args): def __exit__(self, *args):
self.capture_dev.release() self.capture_dev.release()
@ -116,13 +120,13 @@ class USBCamPlugin(shepherd.plugin.Plugin):
confdef.add_def('stabilise_delay', shconf.IntDef(default=5, minval=1, maxval=30, optional=True, confdef.add_def('stabilise_delay', shconf.IntDef(default=5, minval=1, maxval=30, optional=True,
helptext="Number of seconds to wait after starting each camera for exposure and white balance to settle")) helptext="Number of seconds to wait after starting each camera for exposure and white balance to settle"))
array = confdef.add_def('trigger', shconf.TableArrayDef( array = confdef.add_def('trigger', shconf.DictListDef(
helptext="Array of triggers that will use all cameras")) helptext="Array of triggers that will use all cameras"))
array.add_def('hour', shconf.StringDef()) array.add_def('hour', shconf.StringDef())
array.add_def('minute', shconf.StringDef()) array.add_def('minute', shconf.StringDef())
array.add_def('second', shconf.StringDef(default="0", optional=True)) array.add_def('second', shconf.StringDef(default="0", optional=True))
camarray = confdef.add_def('camera', shconf.TableArrayDef( camarray = confdef.add_def('camera', shconf.DictListDef(
helptext="List of cameras to try and connect to. Multiple ports may be listed, and any not connected will be skipped on each trigger.")) helptext="List of cameras to try and connect to. Multiple ports may be listed, and any not connected will be skipped on each trigger."))
camarray.add_def('name', shconf.StringDef(default="", optional=False, camarray.add_def('name', shconf.StringDef(default="", optional=False,
helptext="Name of camera, appended to filename and added to overlay")) helptext="Name of camera, appended to filename and added to overlay"))
@ -137,7 +141,7 @@ class USBCamPlugin(shepherd.plugin.Plugin):
self.hooks = pluginInterface.hooks self.hooks = pluginInterface.hooks
self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"]) self.root_dir = os.path.expanduser(pluginInterface.coreconfig["root_dir"])
self.id = pluginInterface.coreconfig["id"] self.id = pluginInterface.coreconfig["name"]
self.interface.register_hook("pre_cam") self.interface.register_hook("pre_cam")
self.interface.register_hook("post_cam") self.interface.register_hook("post_cam")
@ -150,7 +154,7 @@ class USBCamPlugin(shepherd.plugin.Plugin):
self.gstlock = threading.Lock() self.gstlock = threading.Lock()
if self.config["save_directory"] is "": if self.config["save_directory"] == "":
self.save_directory = os.path.join(self.root_dir, "usbcamera") self.save_directory = os.path.join(self.root_dir, "usbcamera")
else: else:
self.save_directory = self.config["save_directory"] self.save_directory = self.config["save_directory"]
@ -237,7 +241,7 @@ class USBCamPlugin(shepherd.plugin.Plugin):
if self.config["append_id"]: if self.config["append_id"]:
image_filename = image_filename + " " + self.id image_filename = image_filename + " " + self.id
if camera_name is not "": if camera_name != "":
image_filename = image_filename+" "+camera_name image_filename = image_filename+" "+camera_name
image_filename = image_filename + ".jpg" image_filename = image_filename + ".jpg"
image_filename = os.path.join(self.save_directory, image_filename) image_filename = os.path.join(self.save_directory, image_filename)
@ -264,7 +268,6 @@ class USBCamPlugin(shepherd.plugin.Plugin):
size = get_largest_resolution(fmts["MJPG"]) size = get_largest_resolution(fmts["MJPG"])
set_camera_format_opencv(vidcap, "MJPG", size[0], size[1]) set_camera_format_opencv(vidcap, "MJPG", size[0], size[1])
# stream only starts after first grab # stream only starts after first grab
print("Starting cam") print("Starting cam")
@ -285,7 +288,6 @@ class USBCamPlugin(shepherd.plugin.Plugin):
# print("Reading again") # print("Reading again")
# read_flag, frame2 = vidcap.read() # read_flag, frame2 = vidcap.read()
if read_flag: if read_flag:
self._process_image(frame, camera_name) self._process_image(frame, camera_name)
# self._process_image(frame2, camera_name+"(2)") # self._process_image(frame2, camera_name+"(2)")

@ -130,11 +130,11 @@ def save_jobs():
raise Exception("Could not save scheduler job " + raise Exception("Could not save scheduler job " +
job.id+" - Trigger is not a CronTrigger") job.id+" - Trigger is not a CronTrigger")
saved_jobs.append(job.id) saved_jobs.append(job.id)
if hasattr(job,"next_run_time"):
if next_job_time is not None: if next_job_time is not None:
if job.next_run_time < next_job_time: if job.next_run_time < next_job_time:
next_job_time = job.next_run_time next_job_time = job.next_run_time
else: else:
next_job_time = job.next_run_time next_job_time = job.next_run_time
with open(joblist_path+".writing", 'w+') as f: with open(joblist_path+".writing", 'w+') as f:
@ -189,8 +189,8 @@ def _jobs_changed(event):
print(retval) print(retval)
if retval == alarm_str: if retval == alarm_str:
if is_raspberry_pi(): if is_raspberry_pi():
print("Shutting down in 1 minute") print("Shutting down in 2 minutes")
time.sleep(60) time.sleep(120)
subprocess.run(["shutdown","now"]) subprocess.run(["shutdown","now"])
else: else:

@ -0,0 +1,38 @@
[shepherd]
hostname = "DPIRD-test"
id = "DPIRD-test"
plugin_dir = "/home/pi/plugins/"
plugins = ["scout", "picam", "uploader", "mothtrap"]
root_dir = "/home/pi/"
conf_edit_path = "/boot/shepherd.toml"
control_server = "api.shepherd.distreon.net"
api_key = "v2EgvYzx79c8fCP4P7jlWxTZ3pc"
[scout]
boardver = "3"
serialport = "/dev/ttyS0"
[uploader]
[[uploader.destination]]
name = "agricSFTP"
protocol = "sftp"
address = "agric.files.distreon.net"
port = 2222
path = "/mothtraps"
username = "agric"
password = "asherhaze"
[[uploader.bucket]]
name = "imageupload"
open_link_on_new = true
keep_copy = false
destination = "agricSFTP"
[picam]
upload_images = true
upload_bucket = "imageupload"
[[picam.trigger]]
hour = "*"
minute ="*/15"
second = "0"
[mothtrap]
servo_open_pulse = 900
servo_closed_pulse = 2100
servo_open_time = 3
# shepherd_message: Successfully applied this config at:2020-03-29 18:29:28.566836

@ -0,0 +1,50 @@
[shepherd]
hostname = "DPIRD-test"
id = "DPIRD-test"
plugin_dir = "/home/pi/plugins/"
plugins = ["scout", "usbcam", "uploader", "flytrap"]
root_dir = "/home/pi/"
conf_edit_path = "/boot/shepherd.toml"
control_server = "api.shepherd.distreon.net"
api_key = "v2EgvYzx79c8fCP4P7jlWxTZ3pc"
[scout]
boardver = "3"
serialport = "/dev/ttyS0"
[uploader]
[[uploader.destination]]
name = "agricSFTP"
protocol = "sftp"
address = "agric.files.distreon.net"
port = 2222
path = "/mothtraps"
username = "agric"
password = "asherhaze"
[[uploader.bucket]]
name = "imageupload"
open_link_on_new = true
keep_copy = false
destination = "agricSFTP"
[usbcam]
upload_images = true
upload_bucket = "imageupload"
[[usbcam.camera]]
name = "USB1"
usb_port = "1.2"
[[usbcam.camera]]
name = "USB2"
usb_port = "1.3"
[[usbcam.camera]]
name = "USB3"
usb_port = "1.1"
[[usbcam.camera]]
name = "USB"
usb_port = "*"
[[usbcam.trigger]]
hour = "*"
minute ="0"
second = "0"
[flytrap]
servo_open_pulse = 2100
servo_closed_pulse = 900
servo_open_time = 3
# shepherd_message: Successfully applied this config at:2019-12-16 00:59:30.985447
Loading…
Cancel
Save