opensignalbox-interface 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opensignalbox/interface/__init__.py +42 -0
- opensignalbox/interface/adapters/__init__.py +3 -0
- opensignalbox/interface/adapters/base.py +148 -0
- opensignalbox/interface/adapters/controller.py +286 -0
- opensignalbox/interface/adapters/modbus/__init__.py +3 -0
- opensignalbox/interface/adapters/modbus/adapter.py +485 -0
- opensignalbox/interface/assets/favicon.ico +0 -0
- opensignalbox/interface/interfaces/__init__.py +3 -0
- opensignalbox/interface/interfaces/base.py +170 -0
- opensignalbox/interface/interfaces/basicio/__init__.py +3 -0
- opensignalbox/interface/interfaces/basicio/connections.py +239 -0
- opensignalbox/interface/interfaces/basicio/handler.py +1096 -0
- opensignalbox/interface/interfaces/basicio/models.py +165 -0
- opensignalbox/interface/interfaces/bell/__init__.py +3 -0
- opensignalbox/interface/interfaces/bell/connections.py +279 -0
- opensignalbox/interface/interfaces/bell/handler.py +802 -0
- opensignalbox/interface/interfaces/bell/models.py +103 -0
- opensignalbox/interface/interfaces/controller.py +467 -0
- opensignalbox/interface/main.py +187 -0
- opensignalbox/interface/models.py +155 -0
- opensignalbox/interface/routes/__init__.py +4 -0
- opensignalbox/interface/routes/adapters.py +119 -0
- opensignalbox/interface/routes/interfaces.py +104 -0
- opensignalbox/interface/routes.py +204 -0
- opensignalbox/interface/utils.py +29 -0
- opensignalbox/interface/version.py +5 -0
- opensignalbox/interface/web_ui/.gitignore +24 -0
- opensignalbox/interface/web_ui/README.md +5 -0
- opensignalbox/interface/web_ui/WEB_UI_REFACTORING.md +136 -0
- opensignalbox/interface/web_ui/components.json +17 -0
- opensignalbox/interface/web_ui/dist/assets/index-DhsEc6uo.css +2020 -0
- opensignalbox/interface/web_ui/dist/assets/index-t__5JZjf.js +39056 -0
- opensignalbox/interface/web_ui/dist/assets/index-t__5JZjf.js.map +1 -0
- opensignalbox/interface/web_ui/dist/index.html +14 -0
- opensignalbox/interface/web_ui/index.html +13 -0
- opensignalbox/interface/web_ui/package-lock.json +3710 -0
- opensignalbox/interface/web_ui/package.json +39 -0
- opensignalbox/interface/web_ui/src/App.vue +32 -0
- opensignalbox/interface/web_ui/src/assets/favicon.ico +0 -0
- opensignalbox/interface/web_ui/src/assets/index.css +83 -0
- opensignalbox/interface/web_ui/src/components/SharedVariablePicker.vue +112 -0
- opensignalbox/interface/web_ui/src/components/adapters/AdapterList.vue +183 -0
- opensignalbox/interface/web_ui/src/components/adapters/types.ts +35 -0
- opensignalbox/interface/web_ui/src/components/interfaces/InterfaceList.vue +200 -0
- opensignalbox/interface/web_ui/src/components/ui/badge/Badge.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/badge/index.ts +25 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/Breadcrumb.vue +13 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbItem.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbLink.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbList.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbPage.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +21 -0
- opensignalbox/interface/web_ui/src/components/ui/breadcrumb/index.ts +7 -0
- opensignalbox/interface/web_ui/src/components/ui/button/Button.vue +26 -0
- opensignalbox/interface/web_ui/src/components/ui/button/index.ts +35 -0
- opensignalbox/interface/web_ui/src/components/ui/card/Card.vue +21 -0
- opensignalbox/interface/web_ui/src/components/ui/card/CardContent.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/card/CardDescription.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/card/CardFooter.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/card/CardHeader.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/card/CardTitle.vue +18 -0
- opensignalbox/interface/web_ui/src/components/ui/card/index.ts +6 -0
- opensignalbox/interface/web_ui/src/components/ui/checkbox/Checkbox.vue +33 -0
- opensignalbox/interface/web_ui/src/components/ui/checkbox/index.ts +1 -0
- opensignalbox/interface/web_ui/src/components/ui/collapsible/Collapsible.vue +15 -0
- opensignalbox/interface/web_ui/src/components/ui/collapsible/CollapsibleContent.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/collapsible/CollapsibleTrigger.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/collapsible/index.ts +3 -0
- opensignalbox/interface/web_ui/src/components/ui/command/Command.vue +30 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandDialog.vue +21 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandEmpty.vue +20 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandGroup.vue +29 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandInput.vue +33 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandItem.vue +26 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandList.vue +27 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandSeparator.vue +23 -0
- opensignalbox/interface/web_ui/src/components/ui/command/CommandShortcut.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/command/index.ts +9 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/Dialog.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogClose.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogContent.vue +50 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogDescription.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogFooter.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogHeader.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogScrollContent.vue +59 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogTitle.vue +29 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/DialogTrigger.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/dialog/index.ts +9 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenu.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuContent.vue +38 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuItem.vue +28 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +41 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +30 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +33 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +13 -0
- opensignalbox/interface/web_ui/src/components/ui/dropdown-menu/index.ts +16 -0
- opensignalbox/interface/web_ui/src/components/ui/form/FormControl.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/form/FormDescription.vue +20 -0
- opensignalbox/interface/web_ui/src/components/ui/form/FormItem.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/form/FormLabel.vue +23 -0
- opensignalbox/interface/web_ui/src/components/ui/form/FormMessage.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/form/index.ts +7 -0
- opensignalbox/interface/web_ui/src/components/ui/form/injectionKeys.ts +4 -0
- opensignalbox/interface/web_ui/src/components/ui/form/useFormField.ts +30 -0
- opensignalbox/interface/web_ui/src/components/ui/input/Input.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/input/index.ts +1 -0
- opensignalbox/interface/web_ui/src/components/ui/label/Label.vue +27 -0
- opensignalbox/interface/web_ui/src/components/ui/label/index.ts +1 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/NumberField.vue +23 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/NumberFieldContent.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/NumberFieldDecrement.vue +25 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/NumberFieldIncrement.vue +25 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/NumberFieldInput.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/number-field/index.ts +5 -0
- opensignalbox/interface/web_ui/src/components/ui/popover/Popover.vue +15 -0
- opensignalbox/interface/web_ui/src/components/ui/popover/PopoverContent.vue +48 -0
- opensignalbox/interface/web_ui/src/components/ui/popover/PopoverTrigger.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/popover/index.ts +3 -0
- opensignalbox/interface/web_ui/src/components/ui/radio-group/RadioGroup.vue +25 -0
- opensignalbox/interface/web_ui/src/components/ui/radio-group/RadioGroupItem.vue +39 -0
- opensignalbox/interface/web_ui/src/components/ui/radio-group/index.ts +2 -0
- opensignalbox/interface/web_ui/src/components/ui/scroll-area/ScrollArea.vue +29 -0
- opensignalbox/interface/web_ui/src/components/ui/scroll-area/ScrollBar.vue +30 -0
- opensignalbox/interface/web_ui/src/components/ui/scroll-area/index.ts +2 -0
- opensignalbox/interface/web_ui/src/components/ui/select/Select.vue +15 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectContent.vue +53 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectGroup.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectItem.vue +44 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectItemText.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectLabel.vue +13 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectScrollDownButton.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectScrollUpButton.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectSeparator.vue +17 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectTrigger.vue +31 -0
- opensignalbox/interface/web_ui/src/components/ui/select/SelectValue.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/select/index.ts +11 -0
- opensignalbox/interface/web_ui/src/components/ui/separator/Separator.vue +35 -0
- opensignalbox/interface/web_ui/src/components/ui/separator/index.ts +1 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/Sheet.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetClose.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetContent.vue +56 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetDescription.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetFooter.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetHeader.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetTitle.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/SheetTrigger.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/sheet/index.ts +31 -0
- opensignalbox/interface/web_ui/src/components/ui/table/Table.vue +16 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableBody.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableCaption.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableCell.vue +21 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableEmpty.vue +37 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableFooter.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableHead.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableHeader.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/TableRow.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/table/index.ts +9 -0
- opensignalbox/interface/web_ui/src/components/ui/tabs/Tabs.vue +15 -0
- opensignalbox/interface/web_ui/src/components/ui/tabs/TabsContent.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/tabs/TabsList.vue +25 -0
- opensignalbox/interface/web_ui/src/components/ui/tabs/TabsTrigger.vue +29 -0
- opensignalbox/interface/web_ui/src/components/ui/tabs/index.ts +4 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/TagsInput.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/TagsInputInput.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/TagsInputItem.vue +22 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/TagsInputItemDelete.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/TagsInputItemText.vue +19 -0
- opensignalbox/interface/web_ui/src/components/ui/tags-input/index.ts +5 -0
- opensignalbox/interface/web_ui/src/components/ui/textarea/Textarea.vue +24 -0
- opensignalbox/interface/web_ui/src/components/ui/textarea/index.ts +1 -0
- opensignalbox/interface/web_ui/src/components/ui/tooltip/Tooltip.vue +14 -0
- opensignalbox/interface/web_ui/src/components/ui/tooltip/TooltipContent.vue +31 -0
- opensignalbox/interface/web_ui/src/components/ui/tooltip/TooltipProvider.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/tooltip/TooltipTrigger.vue +11 -0
- opensignalbox/interface/web_ui/src/components/ui/tooltip/index.ts +4 -0
- opensignalbox/interface/web_ui/src/libs/utils.ts +6 -0
- opensignalbox/interface/web_ui/src/main.ts +141 -0
- opensignalbox/interface/web_ui/src/views/Overview.vue +11 -0
- opensignalbox/interface/web_ui/src/views/Settings.vue +6 -0
- opensignalbox/interface/web_ui/src/views/SleafordEast.vue +205 -0
- opensignalbox/interface/web_ui/src/views/adapters/modbus/ModbusAdapterEdit.vue +343 -0
- opensignalbox/interface/web_ui/src/views/adapters/modbus/ModbusAdapterView.vue +270 -0
- opensignalbox/interface/web_ui/src/views/interfaces/InterfaceCreate.vue +0 -0
- opensignalbox/interface/web_ui/src/views/interfaces/basicio/BasicIOInterfaceEdit.vue +795 -0
- opensignalbox/interface/web_ui/src/views/interfaces/basicio/BasicIOInterfaceView.vue +648 -0
- opensignalbox/interface/web_ui/src/views/interfaces/bell/BellInterfaceEdit.vue +790 -0
- opensignalbox/interface/web_ui/src/views/interfaces/bell/BellInterfaceView.vue +437 -0
- opensignalbox/interface/web_ui/src/vite-env.d.ts +1 -0
- opensignalbox/interface/web_ui/tailwind.config.js +94 -0
- opensignalbox/interface/web_ui/tsconfig.app.json +27 -0
- opensignalbox/interface/web_ui/tsconfig.json +30 -0
- opensignalbox/interface/web_ui/tsconfig.node.json +12 -0
- opensignalbox/interface/web_ui/tsconfig.tsbuildinfo +1 -0
- opensignalbox/interface/web_ui/vite.config.d.ts +2 -0
- opensignalbox/interface/web_ui/vite.config.js +60 -0
- opensignalbox/interface/web_ui/vite.config.ts +62 -0
- opensignalbox_interface-0.1.0.dist-info/METADATA +49 -0
- opensignalbox_interface-0.1.0.dist-info/RECORD +209 -0
- opensignalbox_interface-0.1.0.dist-info/WHEEL +4 -0
- opensignalbox_interface-0.1.0.dist-info/entry_points.txt +2 -0
- opensignalbox_interface-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main module for opensignalbox-interface.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from anyio import run, sleep
|
|
8
|
+
from opensignalbox.common.messaging import get_messager
|
|
9
|
+
from opensignalbox.common.modules import (
|
|
10
|
+
BaseModule,
|
|
11
|
+
ModuleInfo,
|
|
12
|
+
ModuleStatusEnum,
|
|
13
|
+
UIEntry,
|
|
14
|
+
module_argparser,
|
|
15
|
+
)
|
|
16
|
+
from opensignalbox.interface.adapters.controller import AdapterController
|
|
17
|
+
from opensignalbox.interface.interfaces.controller import InterfaceController
|
|
18
|
+
|
|
19
|
+
from .routes import adapter_router, interface_router
|
|
20
|
+
from .version import __version__
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
messager = get_messager()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Module(BaseModule):
|
|
29
|
+
"""OSB Interface Module."""
|
|
30
|
+
|
|
31
|
+
module_name = "opensignalbox-interface"
|
|
32
|
+
module_file = __file__
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
module_info: ModuleInfo,
|
|
37
|
+
ui_entries: list[UIEntry] | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
if ui_entries is None:
|
|
40
|
+
ui_entries = []
|
|
41
|
+
super().__init__(
|
|
42
|
+
module_info=module_info,
|
|
43
|
+
ui_entries=ui_entries,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Initialize controllers
|
|
47
|
+
self.adapter_controller = AdapterController(self.module_info.config_path)
|
|
48
|
+
self.fastapi_app.state.adapter_controller = self.adapter_controller
|
|
49
|
+
self.interface_controller = InterfaceController(self.module_info.config_path, self.shared_variables, self.adapter_controller)
|
|
50
|
+
self.fastapi_app.state.interface_controller = self.interface_controller
|
|
51
|
+
|
|
52
|
+
# Include API routers
|
|
53
|
+
self.fastapi_app.include_router(
|
|
54
|
+
adapter_router, prefix="/api/adapters",
|
|
55
|
+
tags=["Adapters"]
|
|
56
|
+
)
|
|
57
|
+
self.fastapi_app.include_router(
|
|
58
|
+
interface_router, prefix="/api/interfaces",
|
|
59
|
+
tags=["Interfaces"]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def setup(self) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Module startup code.
|
|
65
|
+
"""
|
|
66
|
+
# Register any interface handlers and adapter factories
|
|
67
|
+
self._register_handlers()
|
|
68
|
+
|
|
69
|
+
# Register interface-specific routers
|
|
70
|
+
self._register_interface_routers()
|
|
71
|
+
|
|
72
|
+
# Register adapter-specific routers
|
|
73
|
+
self._register_adapter_routers()
|
|
74
|
+
|
|
75
|
+
# Load configurations
|
|
76
|
+
self.adapter_controller.load_from_json()
|
|
77
|
+
self.interface_controller.load_from_json()
|
|
78
|
+
|
|
79
|
+
# Connect interfaces
|
|
80
|
+
if self.interface_controller.module_config.connect_on_startup:
|
|
81
|
+
self.interface_controller.connect_all()
|
|
82
|
+
|
|
83
|
+
return super().setup()
|
|
84
|
+
|
|
85
|
+
def _register_interface_routers(self) -> None:
|
|
86
|
+
"""Register interface-specific routers."""
|
|
87
|
+
# Get all interface routers and include them in FastAPI app
|
|
88
|
+
for interface_type, router in self.interface_controller.get_interface_routers().items():
|
|
89
|
+
# Convert enum to lowercase string for URL path
|
|
90
|
+
interface_path = interface_type.value.lower()
|
|
91
|
+
self.fastapi_app.include_router(
|
|
92
|
+
router, prefix=f"/api/interfaces/{interface_path}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def _register_adapter_routers(self) -> None:
|
|
96
|
+
"""Register adapter-specific routers."""
|
|
97
|
+
# Get all adapter routers and include them in FastAPI app
|
|
98
|
+
for adapter_type, router in self.adapter_controller.get_adapter_routers().items():
|
|
99
|
+
# Convert enum to lowercase string for URL path
|
|
100
|
+
adapter_path = adapter_type.value.lower()
|
|
101
|
+
self.fastapi_app.include_router(
|
|
102
|
+
router, prefix=f"/api/adapters/{adapter_path}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _register_handlers(self) -> None:
|
|
106
|
+
"""Register interface handlers and adapter factories."""
|
|
107
|
+
self.interface_controller.register_interface_handlers()
|
|
108
|
+
self.adapter_controller.register_adapter_factories()
|
|
109
|
+
|
|
110
|
+
def systick_update(self, tick_json: str) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Module update logic executed once per clock tick.
|
|
113
|
+
"""
|
|
114
|
+
super().systick_update(tick_json)
|
|
115
|
+
if self.interface_controller.module_config.sync_on_systick:
|
|
116
|
+
self.interface_controller.sync_interfaces()
|
|
117
|
+
|
|
118
|
+
async def user_task(self) -> None:
|
|
119
|
+
"""Background task for periodic interface updates."""
|
|
120
|
+
while True:
|
|
121
|
+
if (
|
|
122
|
+
not self.interface_controller.module_config.sync_on_systick
|
|
123
|
+
and self.module_info.status == ModuleStatusEnum.READY
|
|
124
|
+
):
|
|
125
|
+
await self.interface_controller.tick()
|
|
126
|
+
else:
|
|
127
|
+
await sleep(1)
|
|
128
|
+
|
|
129
|
+
def teardown(self) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Module teardown code.
|
|
132
|
+
"""
|
|
133
|
+
# Disconnect interfaces and adapters
|
|
134
|
+
self.interface_controller.disconnect_all()
|
|
135
|
+
self.adapter_controller.disconnect_all()
|
|
136
|
+
|
|
137
|
+
# Save configurations
|
|
138
|
+
self.adapter_controller.save_to_json()
|
|
139
|
+
self.interface_controller.save_to_json()
|
|
140
|
+
|
|
141
|
+
def load_state(self) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Load module state.
|
|
144
|
+
"""
|
|
145
|
+
return super().load_state()
|
|
146
|
+
|
|
147
|
+
def save_state(self) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Save module state.
|
|
150
|
+
"""
|
|
151
|
+
return super().save_state()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
args = module_argparser()
|
|
155
|
+
instance_name: str = args.name
|
|
156
|
+
|
|
157
|
+
ui_entries = [
|
|
158
|
+
UIEntry(
|
|
159
|
+
category="operations", subcategory="Interfaces", name="Interface List", path="/"
|
|
160
|
+
),
|
|
161
|
+
UIEntry(
|
|
162
|
+
category="configuration",
|
|
163
|
+
subcategory="Interfaces",
|
|
164
|
+
name="Interface Settings",
|
|
165
|
+
path="/settings",
|
|
166
|
+
),
|
|
167
|
+
]
|
|
168
|
+
module_info = ModuleInfo(
|
|
169
|
+
instance_name=args.name,
|
|
170
|
+
module_name=Module.module_name,
|
|
171
|
+
version=__version__,
|
|
172
|
+
address=args.address,
|
|
173
|
+
port=args.port,
|
|
174
|
+
session_id=args.session_id,
|
|
175
|
+
framework_address=args.framework_address,
|
|
176
|
+
config_path=args.configdir,
|
|
177
|
+
)
|
|
178
|
+
module = Module(module_info=module_info, ui_entries=ui_entries)
|
|
179
|
+
module.setup()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def entry_point():
|
|
183
|
+
run(module.run, backend="trio") # type: ignore
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
entry_point()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base models for the opensignalbox-interface module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from opensignalbox.common.models import CamelModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterfaceTypeEnum(str, Enum):
|
|
12
|
+
"""Enum for interface types"""
|
|
13
|
+
|
|
14
|
+
BASICIO = "BasicIO"
|
|
15
|
+
BELL = "Bell"
|
|
16
|
+
# Add more interface types here as needed
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AdapterTypeEnum(str, Enum):
|
|
20
|
+
"""Enum for adapter types"""
|
|
21
|
+
|
|
22
|
+
MODBUS = "Modbus"
|
|
23
|
+
# Add more adapter types here as needed
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ModuleConfig(CamelModel):
|
|
27
|
+
input_update_period: float = 0.1
|
|
28
|
+
output_update_period: float = 1
|
|
29
|
+
sync_on_systick: bool = False
|
|
30
|
+
connect_on_startup: bool = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AdapterBase(CamelModel):
|
|
34
|
+
"""Base model for all adapters"""
|
|
35
|
+
|
|
36
|
+
system_name: str
|
|
37
|
+
user_name: str
|
|
38
|
+
description: str = ""
|
|
39
|
+
adapter_type: AdapterTypeEnum
|
|
40
|
+
enabled: bool = True
|
|
41
|
+
error: Optional[str] = None
|
|
42
|
+
# Dynamic fields for different adapter types
|
|
43
|
+
is_network: Optional[bool] = None
|
|
44
|
+
serial_port: Optional[str] = None
|
|
45
|
+
baud: Optional[int] = None
|
|
46
|
+
address: Optional[str] = None
|
|
47
|
+
network_port: Optional[int] = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AdapterModel(AdapterBase):
|
|
51
|
+
"""Runtime model for adapters with additional operational fields"""
|
|
52
|
+
|
|
53
|
+
is_connected: bool = False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AdapterCreate(CamelModel):
|
|
57
|
+
"""API model for creating adapters. Omits adapter type."""
|
|
58
|
+
|
|
59
|
+
system_name: str
|
|
60
|
+
user_name: str
|
|
61
|
+
description: str = ""
|
|
62
|
+
enabled: bool = True
|
|
63
|
+
error: Optional[str] = None
|
|
64
|
+
# Dynamic fields for different adapter types
|
|
65
|
+
is_network: Optional[bool] = None
|
|
66
|
+
serial_port: Optional[str] = None
|
|
67
|
+
baud: Optional[int] = None
|
|
68
|
+
address: Optional[str] = None
|
|
69
|
+
network_port: Optional[int] = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AdapterPublic(AdapterBase):
|
|
73
|
+
"""API model for adapter responses"""
|
|
74
|
+
|
|
75
|
+
is_connected: bool = False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AdapterUpdate(CamelModel):
|
|
79
|
+
"""API model for adapter updates"""
|
|
80
|
+
|
|
81
|
+
system_name: Optional[str] = None
|
|
82
|
+
user_name: Optional[str] = None
|
|
83
|
+
description: Optional[str] = None
|
|
84
|
+
enabled: Optional[bool] = None
|
|
85
|
+
is_network: Optional[bool] = None
|
|
86
|
+
serial_port: Optional[str] = None
|
|
87
|
+
baud: Optional[int] = None
|
|
88
|
+
address: Optional[str] = None
|
|
89
|
+
network_port: Optional[int] = None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class InterfaceBase(CamelModel):
|
|
93
|
+
"""Base model for all interfaces with common fields"""
|
|
94
|
+
|
|
95
|
+
system_name: str
|
|
96
|
+
user_name: str
|
|
97
|
+
description: str = ""
|
|
98
|
+
adapter_name: Optional[str] = None
|
|
99
|
+
error: Optional[str] = None
|
|
100
|
+
enabled: bool = True
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Interface(InterfaceBase):
|
|
104
|
+
"""Runtime model for all interfaces"""
|
|
105
|
+
|
|
106
|
+
interface_type: InterfaceTypeEnum
|
|
107
|
+
is_connected: bool = False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class InterfacePublic(Interface):
|
|
111
|
+
"""API model for interface responses"""
|
|
112
|
+
|
|
113
|
+
is_connected: bool = False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class InterfaceUpdate(CamelModel):
|
|
117
|
+
"""API model for interface updates"""
|
|
118
|
+
|
|
119
|
+
system_name: Optional[str] = None
|
|
120
|
+
user_name: Optional[str] = None
|
|
121
|
+
description: Optional[str] = None
|
|
122
|
+
adapter_name: Optional[str] = None
|
|
123
|
+
enabled: Optional[bool] = None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ConnectionTypeInfo(CamelModel):
|
|
127
|
+
"""Information about a connection type"""
|
|
128
|
+
|
|
129
|
+
interface_type: InterfaceTypeEnum
|
|
130
|
+
adapter_type: AdapterTypeEnum
|
|
131
|
+
connection_class: str
|
|
132
|
+
name: str
|
|
133
|
+
description: str
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class AdapterTypeInfo(CamelModel):
|
|
137
|
+
"""Information about an adapter type"""
|
|
138
|
+
|
|
139
|
+
adapter_type: AdapterTypeEnum
|
|
140
|
+
name: str
|
|
141
|
+
description: str
|
|
142
|
+
supported_connection_types: List[str]
|
|
143
|
+
required_fields: List[str]
|
|
144
|
+
optional_fields: List[str]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class InterfaceTypeInfo(CamelModel):
|
|
148
|
+
"""Information about an interface type"""
|
|
149
|
+
|
|
150
|
+
interface_type: InterfaceTypeEnum
|
|
151
|
+
name: str
|
|
152
|
+
description: str
|
|
153
|
+
supported_connection_types: List[str]
|
|
154
|
+
required_fields: List[str]
|
|
155
|
+
optional_fields: List[str]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API routes for the opensignalbox.interface module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Annotated, List, Optional
|
|
7
|
+
|
|
8
|
+
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
|
9
|
+
from opensignalbox.common.variables import SharedVariableCollection
|
|
10
|
+
from opensignalbox.interface.adapters.controller import AdapterController
|
|
11
|
+
from opensignalbox.interface.models import (
|
|
12
|
+
AdapterPublic,
|
|
13
|
+
AdapterTypeInfo,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
def get_adapter_controller(request: Request) -> AdapterController:
|
|
19
|
+
return request.app.state.adapter_controller
|
|
20
|
+
|
|
21
|
+
AdapterControllerDependency = Annotated[AdapterController, Depends(get_adapter_controller)]
|
|
22
|
+
|
|
23
|
+
def get_shared_variables(request: Request) -> SharedVariableCollection:
|
|
24
|
+
return request.app.state.shared_variables
|
|
25
|
+
|
|
26
|
+
SharedVariablesDependency = Annotated[SharedVariableCollection, Depends(get_shared_variables)]
|
|
27
|
+
|
|
28
|
+
adapter_router = APIRouter()
|
|
29
|
+
|
|
30
|
+
@adapter_router.get("/", response_model=List[AdapterPublic])
|
|
31
|
+
async def get_adapters(
|
|
32
|
+
adapters: AdapterControllerDependency,
|
|
33
|
+
adapter_type: Optional[str] = Query(
|
|
34
|
+
None, description="Filter by adapter type"
|
|
35
|
+
),
|
|
36
|
+
):
|
|
37
|
+
"""Get all adapters, optionally filtered by type."""
|
|
38
|
+
adapters_list = []
|
|
39
|
+
for adapter in adapters.get_all().values():
|
|
40
|
+
# Filter by type if specified
|
|
41
|
+
if adapter_type and adapter.adapter_type != adapter_type:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
# Convert BaseAdapter to AdapterPublic
|
|
45
|
+
adapter_dict = {
|
|
46
|
+
"system_name": adapter.system_name,
|
|
47
|
+
"user_name": adapter.user_name,
|
|
48
|
+
"description": adapter.description,
|
|
49
|
+
"adapter_type": adapter.adapter_type,
|
|
50
|
+
"enabled": adapter.enabled,
|
|
51
|
+
"is_connected": adapter.is_connected,
|
|
52
|
+
}
|
|
53
|
+
adapters_list.append(AdapterPublic(**adapter_dict))
|
|
54
|
+
return adapters_list
|
|
55
|
+
|
|
56
|
+
@adapter_router.put("/{system_name}/enable", response_model=AdapterPublic)
|
|
57
|
+
async def enable_adapter(system_name: str, adapters: AdapterControllerDependency):
|
|
58
|
+
"""Enable a specific adapter."""
|
|
59
|
+
adapter = adapters.get(system_name)
|
|
60
|
+
if not adapter:
|
|
61
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
|
62
|
+
adapter.enabled = True
|
|
63
|
+
adapter_dict = {
|
|
64
|
+
"system_name": adapter.system_name,
|
|
65
|
+
"user_name": adapter.user_name,
|
|
66
|
+
"description": adapter.description,
|
|
67
|
+
"adapter_type": adapter.adapter_type,
|
|
68
|
+
"enabled": adapter.enabled,
|
|
69
|
+
"is_connected": adapter.is_connected,
|
|
70
|
+
}
|
|
71
|
+
return AdapterPublic(**adapter_dict)
|
|
72
|
+
|
|
73
|
+
@adapter_router.put(
|
|
74
|
+
"/{system_name}/disable", response_model=AdapterPublic
|
|
75
|
+
)
|
|
76
|
+
async def disable_adapter(system_name: str, adapters: AdapterControllerDependency):
|
|
77
|
+
"""Disable a specific adapter."""
|
|
78
|
+
adapter = adapters.get(system_name)
|
|
79
|
+
if not adapter:
|
|
80
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
|
81
|
+
adapter.enabled = False
|
|
82
|
+
adapter_dict = {
|
|
83
|
+
"system_name": adapter.system_name,
|
|
84
|
+
"user_name": adapter.user_name,
|
|
85
|
+
"description": adapter.description,
|
|
86
|
+
"adapter_type": adapter.adapter_type,
|
|
87
|
+
"enabled": adapter.enabled,
|
|
88
|
+
"is_connected": adapter.is_connected,
|
|
89
|
+
}
|
|
90
|
+
return AdapterPublic(**adapter_dict)
|
|
91
|
+
|
|
92
|
+
@adapter_router.put(
|
|
93
|
+
"/{system_name}/connect", response_model=AdapterPublic
|
|
94
|
+
)
|
|
95
|
+
async def connect_adapter(system_name: str, adapters: AdapterControllerDependency):
|
|
96
|
+
"""Connect a specific adapter."""
|
|
97
|
+
adapter = adapters.get(system_name)
|
|
98
|
+
if not adapter:
|
|
99
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
|
100
|
+
if adapter.connect():
|
|
101
|
+
return adapter
|
|
102
|
+
else:
|
|
103
|
+
raise HTTPException(status_code=500, detail="Failed to connect adapter")
|
|
104
|
+
|
|
105
|
+
@adapter_router.put(
|
|
106
|
+
"/{system_name}/disconnect", response_model=AdapterPublic
|
|
107
|
+
)
|
|
108
|
+
async def disconnect_adapter(system_name: str, adapters: AdapterControllerDependency):
|
|
109
|
+
"""Disconnect a specific adapter."""
|
|
110
|
+
adapter = adapters.get(system_name)
|
|
111
|
+
if not adapter:
|
|
112
|
+
raise HTTPException(status_code=404, detail="Adapter not found")
|
|
113
|
+
adapter.disconnect()
|
|
114
|
+
return adapter
|
|
115
|
+
|
|
116
|
+
@adapter_router.get("/types/adapters", response_model=List[AdapterTypeInfo])
|
|
117
|
+
async def get_adapter_types(adapters: AdapterControllerDependency):
|
|
118
|
+
"""Get all available adapter types with their metadata."""
|
|
119
|
+
return adapters.get_adapter_type_infos()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API routes for the opensignalbox-interface module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Annotated, List, Optional
|
|
7
|
+
|
|
8
|
+
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
|
9
|
+
from opensignalbox.common.variables import SharedVariableCollection
|
|
10
|
+
from opensignalbox.interface.interfaces.controller import InterfaceController
|
|
11
|
+
from opensignalbox.interface.models import (
|
|
12
|
+
InterfacePublic,
|
|
13
|
+
InterfaceTypeInfo,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
def get_interface_controller(request: Request) -> InterfaceController:
|
|
19
|
+
return request.app.state.interface_controller
|
|
20
|
+
|
|
21
|
+
InterfaceControllerDependency = Annotated[InterfaceController, Depends(get_interface_controller)]
|
|
22
|
+
|
|
23
|
+
def get_shared_variables(request: Request) -> SharedVariableCollection:
|
|
24
|
+
return request.app.state.shared_variables
|
|
25
|
+
|
|
26
|
+
SharedVariablesDependency = Annotated[SharedVariableCollection, Depends(get_shared_variables)]
|
|
27
|
+
|
|
28
|
+
interface_router = APIRouter()
|
|
29
|
+
|
|
30
|
+
@interface_router.get("/", response_model=List[InterfacePublic])
|
|
31
|
+
async def get_interfaces(
|
|
32
|
+
interfaces: InterfaceControllerDependency,
|
|
33
|
+
interface_type: Optional[str] = Query(
|
|
34
|
+
None, description="Filter by interface type"
|
|
35
|
+
),
|
|
36
|
+
):
|
|
37
|
+
"""Get all interfaces, optionally filtered by type."""
|
|
38
|
+
interfaces_list = []
|
|
39
|
+
for interface in interfaces.get_all().values():
|
|
40
|
+
# Filter by type if specified
|
|
41
|
+
if interface_type and interface.interface_type != interface_type:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
interfaces_list.append(interface)
|
|
45
|
+
return interfaces_list
|
|
46
|
+
|
|
47
|
+
@interface_router.put(
|
|
48
|
+
"/{system_name}/disable", response_model=InterfacePublic
|
|
49
|
+
)
|
|
50
|
+
async def disable_interface(system_name: str, interfaces: InterfaceControllerDependency):
|
|
51
|
+
"""Disable a specific interface."""
|
|
52
|
+
interface = interfaces.get(system_name)
|
|
53
|
+
if not interface:
|
|
54
|
+
raise HTTPException(status_code=404, detail="Interface not found")
|
|
55
|
+
interface.enabled = False
|
|
56
|
+
return interface
|
|
57
|
+
|
|
58
|
+
@interface_router.put(
|
|
59
|
+
"/{system_name}/enable", response_model=InterfacePublic
|
|
60
|
+
)
|
|
61
|
+
async def enable_interface(system_name: str, interfaces: InterfaceControllerDependency):
|
|
62
|
+
"""Enable a specific interface."""
|
|
63
|
+
interface = interfaces.get(system_name)
|
|
64
|
+
if not interface:
|
|
65
|
+
raise HTTPException(status_code=404, detail="Interface not found")
|
|
66
|
+
interface.enabled = True
|
|
67
|
+
return interface
|
|
68
|
+
|
|
69
|
+
@interface_router.put(
|
|
70
|
+
"/{system_name}/connect", response_model=InterfacePublic
|
|
71
|
+
)
|
|
72
|
+
async def connect_interface(system_name: str, interfaces: InterfaceControllerDependency):
|
|
73
|
+
"""Connect a specific interface."""
|
|
74
|
+
interface = interfaces.get(system_name)
|
|
75
|
+
if not interface:
|
|
76
|
+
raise HTTPException(status_code=404, detail="Interface not found")
|
|
77
|
+
handler = interfaces.interface_handlers.get(interface.interface_type)
|
|
78
|
+
if handler and handler.connect(interface):
|
|
79
|
+
return interface
|
|
80
|
+
else:
|
|
81
|
+
raise HTTPException(
|
|
82
|
+
status_code=500, detail="Failed to connect interface"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
@interface_router.put(
|
|
86
|
+
"/{system_name}/disconnect", response_model=InterfacePublic
|
|
87
|
+
)
|
|
88
|
+
async def disconnect_interface(system_name: str, interfaces: InterfaceControllerDependency):
|
|
89
|
+
"""Disconnect a specific interface."""
|
|
90
|
+
interface = interfaces.get(system_name)
|
|
91
|
+
if not interface:
|
|
92
|
+
raise HTTPException(status_code=404, detail="Interface not found")
|
|
93
|
+
handler = interfaces.interface_handlers.get(interface.interface_type)
|
|
94
|
+
if handler and handler.disconnect(interface):
|
|
95
|
+
return interface
|
|
96
|
+
else:
|
|
97
|
+
raise HTTPException(
|
|
98
|
+
status_code=500, detail="Failed to disconnect interface"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@interface_router.get("/types/interfaces", response_model=List[InterfaceTypeInfo])
|
|
102
|
+
async def get_interface_types(interfaces: InterfaceControllerDependency):
|
|
103
|
+
"""Get all available interface types with their metadata."""
|
|
104
|
+
return interfaces.get_interface_type_infos()
|