diff --git a/README.adoc b/README.adoc index d70c0df16..e3932912d 100644 --- a/README.adoc +++ b/README.adoc @@ -17,8 +17,8 @@ While code is divided in specific areas most ot it is present under `bundles` fo ** `org.connectorio.addons.binding` - common/shared code for binding. ** `org.connectorio.addons.binding.askoheat` - integration of Askoheat heaters (experimental). ** `org.connectorio.addons.binding.amsads` - implementation of ADS based integration for Beckhoff PLCs. - ** `org.connectorio.addons.binding.can` - generic purpose CAN binding (experimental). ** `org.connectorio.addons.binding.bacnet` - communication with BACnet enabled HVAC equipment and other commercial real estate hardware. + ** `org.connectorio.addons.binding.canbus` - generic purpose CAN bus binding (experimental). ** `org.connectorio.addons.binding.canopen` - integration of CANopen enabled deployments through SocketCAN. ** `org.connectorio.addons.binding.canopen.ta` - integration for https://ta.co.at[Technische Alternative] hardware (it is based on CANopen). ** `org.connectorio.addons.binding.plc4x` - integration with http://plc4x.apache.org[Apache PLC4X] project. diff --git a/bundles/org.connectorio.addons.binding.amsads/src/main/java/org/connectorio/addons/binding/amsads/internal/handler/channel/AdsChannelHandlerBase.java b/bundles/org.connectorio.addons.binding.amsads/src/main/java/org/connectorio/addons/binding/amsads/internal/handler/channel/AdsChannelHandlerBase.java index e5d662937..d59991c00 100644 --- a/bundles/org.connectorio.addons.binding.amsads/src/main/java/org/connectorio/addons/binding/amsads/internal/handler/channel/AdsChannelHandlerBase.java +++ b/bundles/org.connectorio.addons.binding.amsads/src/main/java/org/connectorio/addons/binding/amsads/internal/handler/channel/AdsChannelHandlerBase.java @@ -49,7 +49,8 @@ protected AdsChannelHandlerBase(Thing thing, ThingHandlerCallback callback, Chan this.thing = thing; this.callback = callback; this.channel = channel; - this.refreshInterval = Optional.ofNullable(channel.getConfiguration()) + this.refreshInterval = Optional.ofNullable(channel) + .map(Channel::getConfiguration) .map(cfg -> cfg.get("refreshInterval")) .map(val -> val instanceof Long ? (Long) val : Long.parseLong("" + val)) .orElse(null); diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/discovery/CANInterfaceDiscoveryDelegate.java b/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/discovery/CANInterfaceDiscoveryDelegate.java deleted file mode 100644 index efbb55afd..000000000 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/discovery/CANInterfaceDiscoveryDelegate.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.can.discovery; - -import org.connectorio.addons.binding.can.CANInterface; - -/** - * Detection of available CAN interfaces is an abstract act which does not bring connectivity yet. - * - * This interface is intended to let implementers know that given CAN interface is available and can be utilized. - */ -@Deprecated -public interface CANInterfaceDiscoveryDelegate { - - /** - * Notify about availability of given CAN interface. - * - * @param iface CAN interface. - */ - void interfaceAvailable(CANInterface iface); - -} diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/internal/discovery/DBusCANInterfaceDiscoveryService.java b/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/internal/discovery/DBusCANInterfaceDiscoveryService.java deleted file mode 100644 index 6a84a6205..000000000 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/internal/discovery/DBusCANInterfaceDiscoveryService.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.can.internal.discovery; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.CopyOnWriteArrayList; -import org.connectorio.addons.binding.can.CANInterface; -import org.connectorio.addons.binding.can.CANInterfaceTypes; -import org.connectorio.addons.binding.can.discovery.CANInterfaceDiscoveryDelegate; -import org.freedesktop.dbus.DBusPath; -import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.connections.impl.DBusConnection.DBusBusType; -import org.freedesktop.dbus.interfaces.ObjectManager; -import org.freedesktop.dbus.types.Variant; -import org.openhab.core.config.discovery.AbstractDiscoveryService; -import org.openhab.core.config.discovery.DiscoveryService; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Deprecated -@Component(service = DiscoveryService.class) -public class DBusCANInterfaceDiscoveryService extends AbstractDiscoveryService implements DiscoveryService { - - public static final String DEVICE_TYPE = "org.freedesktop.NetworkManager.Device"; - public static final String DEVICE_PATH = "/org/freedesktop/NetworkManager/Devices/"; - public static final String NETWORK_MANAGER_BUS = "org.freedesktop.NetworkManager"; - - public static final String DRIVER_PROPERTY = "Driver"; - public static final String DRIVER_VCAN = "vcan"; - public static final String DRIVER_GS_USB = "gs_usb"; - public static final String INTERFACE_PROPERTY = "Interface"; - - private final Logger logger = LoggerFactory.getLogger(DBusCANInterfaceDiscoveryService.class); - private final List participants = new CopyOnWriteArrayList<>(); - - public DBusCANInterfaceDiscoveryService() { - super(null, 30); - } - - @Override - protected void startBackgroundDiscovery() { - scanInterfaces(); - } - - @Override - protected void startScan() { - scanInterfaces(); - } - - private void scanInterfaces() { - try (DBusConnection connection = DBusConnection.getConnection(DBusBusType.SYSTEM, false, DBusConnection.TCP_CONNECT_TIMEOUT)) { - ObjectManager nm = connection.getRemoteObject(NETWORK_MANAGER_BUS, "/org/freedesktop", ObjectManager.class); - - for (Entry>>> pathEntry : nm.GetManagedObjects().entrySet()) { - DBusPath key = pathEntry.getKey(); - - if (key.getPath().startsWith(DEVICE_PATH)) { - logger.debug("Inspecting dbus path: {}", key); - - Map>> value = pathEntry.getValue(); - for (Entry>> nodeEntry : value.entrySet()) { - logger.trace("Inspection of device node: {}", nodeEntry); - if (DEVICE_TYPE.equals(nodeEntry.getKey())) { - logger.trace("Found device node, checking if its can interface: {}", nodeEntry.getKey()); - - final Variant driver = nodeEntry.getValue().get(DRIVER_PROPERTY); - final Variant name = nodeEntry.getValue().get(INTERFACE_PROPERTY); - - if (DRIVER_VCAN.equals(driver.getValue())) { - notify(driver, name, "Virtual CAN interface"); //.ifPresent(this::thingDiscovered); - } else if (DRIVER_GS_USB.equals(driver.getValue())) { - notify(driver, name, "Socket CAN interface"); //.ifPresent(this::thingDiscovered); - } - } - } - } - } - - connection.disconnect(); - } catch (Exception e) { - logger.error("Discovery of can interfaces via dbus failed", e); - } - } - - private void notify(Variant driver, Variant name, String label) { - if (name != null && CharSequence.class.equals(name.getType())) { - final CANInterface canInterface = new CANInterface(((CharSequence) name.getValue()).toString(), CANInterfaceTypes.SOCKET_CAN); - for (CANInterfaceDiscoveryDelegate participant : participants) { - participant.interfaceAvailable(canInterface); - } - } else { - logger.trace("Unidentifiable CAN interface {} which uses supported driver {}. Ignoring", name, driver); - } - } - - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) - public void addDiscoveryDelegate(CANInterfaceDiscoveryDelegate participant) { - participants.add(participant); - } - - public void removeDiscoveryDelegate(CANInterfaceDiscoveryDelegate participant) { - participants.remove(participant); - } - -} diff --git a/bundles/org.connectorio.addons.binding.canbus/pom.xml b/bundles/org.connectorio.addons.binding.canbus/pom.xml new file mode 100644 index 000000000..1a7a70106 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/pom.xml @@ -0,0 +1,91 @@ + + + + + 4.0.0 + + + org.connectorio.addons + bundles + 3.0.0-SNAPSHOT + + + org.connectorio.addons.binding.canbus + bundle + + ConnectorIO - Addons - Binding - CAN bus + CAN bus integration. + + + + org.connectorio.addons + org.connectorio.bittpl + + + org.connectorio.addons + org.connectorio.addons.network.can + + + org.connectorio.addons + org.connectorio.addons.binding.plc4x + + + org.connectorio.addons + org.connectorio.units + + + org.apache.plc4x + plc4j-driver-can + + + + org.openhab.core.bundles + org.openhab.core.config.discovery + + + org.osgi + org.osgi.service.component.annotations + + + + org.connectorio.addons + org.connectorio.addons.feature.plc4x + features + xml + + + + org.connectorio.addons + org.connectorio.addons.binding.test + + + org.junit.jupiter + junit-jupiter-api + + + org.mockito + mockito-junit-jupiter + + + org.assertj + assertj-core + + + + diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CANbusBindingConstants.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CANbusBindingConstants.java new file mode 100644 index 000000000..49a1c74fa --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CANbusBindingConstants.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO sp. z o.o. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * https://www.gnu.org/licenses/gpl-3.0.txt + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package org.connectorio.addons.binding.canbus; + + +import java.util.Set; +import org.connectorio.addons.binding.BaseBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +public interface CANbusBindingConstants extends BaseBindingConstants { + + String BINDING_ID = BaseBindingConstants.identifier("canbus"); + + ThingTypeUID SOCKETCAN_BRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "socketcan"); + ThingTypeUID MESSAGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "message"); + Set SUPPORTED_THINGS = Set.of(SOCKETCAN_BRIDGE_THING_TYPE, MESSAGE_THING_TYPE); +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CanConnection.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CanConnection.java new file mode 100644 index 000000000..555542bc4 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/CanConnection.java @@ -0,0 +1,14 @@ +package org.connectorio.addons.binding.canbus; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.function.Consumer; + +public interface CanConnection { + + CompletableFuture write(int cob, byte[] data); + + // cancellable subscription + Future subscribe(int cob, Consumer callback); + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/MessageConfiguration.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/MessageConfiguration.java new file mode 100644 index 000000000..aea321ef0 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/MessageConfiguration.java @@ -0,0 +1,8 @@ +package org.connectorio.addons.binding.canbus.config; + +public class MessageConfiguration { + + int cob; + String template; + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/SocketCANConfiguration.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/SocketCANConfiguration.java new file mode 100644 index 000000000..33516f8fb --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/SocketCANConfiguration.java @@ -0,0 +1,9 @@ +package org.connectorio.addons.binding.canbus.config; + +import org.connectorio.addons.binding.config.Configuration; + +public class SocketCANConfiguration implements Configuration { + + public String name; + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/BaseOffsetConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/BaseOffsetConfig.java new file mode 100644 index 000000000..d321fc245 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/BaseOffsetConfig.java @@ -0,0 +1,12 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public class BaseOffsetConfig implements OffsetConfig { + + public int offset; + + @Override + public int offset() { + return offset; + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ByteChannelConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ByteChannelConfig.java new file mode 100644 index 000000000..0b5d78b8d --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ByteChannelConfig.java @@ -0,0 +1,12 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public class ByteChannelConfig extends BaseOffsetConfig implements LengthConfig { + + public int length; + + @Override + public int length() { + return length; + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ContactChannelConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ContactChannelConfig.java new file mode 100644 index 000000000..d40795301 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/ContactChannelConfig.java @@ -0,0 +1,8 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public class ContactChannelConfig extends BaseOffsetConfig { + + String openValue; + String closedValue; + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/LengthConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/LengthConfig.java new file mode 100644 index 000000000..cf6338da2 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/LengthConfig.java @@ -0,0 +1,7 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public interface LengthConfig { + + int length(); + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/NumberChannelConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/NumberChannelConfig.java new file mode 100644 index 000000000..ee0a43f0b --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/NumberChannelConfig.java @@ -0,0 +1,16 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +import org.apache.plc4x.java.genericcan.readwrite.GenericCANDataType; + +public class NumberChannelConfig extends BaseOffsetConfig implements LengthConfig { + + public int length; + + public GenericCANDataType type; + + @Override + public int length() { + return length; + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/OffsetConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/OffsetConfig.java new file mode 100644 index 000000000..9b8e98bea --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/OffsetConfig.java @@ -0,0 +1,7 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public interface OffsetConfig { + + int offset(); + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/SwitchChannelConfig.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/SwitchChannelConfig.java new file mode 100644 index 000000000..f9b0b51e1 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/config/channel/SwitchChannelConfig.java @@ -0,0 +1,8 @@ +package org.connectorio.addons.binding.canbus.config.channel; + +public class SwitchChannelConfig extends BaseOffsetConfig { + + String onValue; + String offValue; + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/discovery/CANDiscoveryParticipant.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/discovery/CANDiscoveryParticipant.java new file mode 100644 index 000000000..11d2e4dd4 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/discovery/CANDiscoveryParticipant.java @@ -0,0 +1,6 @@ +package org.connectorio.addons.binding.canbus.discovery; + +// TBD +public interface CANDiscoveryParticipant { + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/DefaultCanConnection.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/DefaultCanConnection.java new file mode 100644 index 000000000..c52e89b39 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/DefaultCanConnection.java @@ -0,0 +1,83 @@ +package org.connectorio.addons.binding.canbus.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import org.apache.plc4x.java.api.PlcConnection; +import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; +import org.apache.plc4x.java.api.model.PlcConsumerRegistration; +import org.apache.plc4x.java.api.value.PlcValue; +import org.apache.plc4x.java.can.generic.tag.GenericCANTag; +import org.apache.plc4x.java.genericcan.readwrite.GenericCANDataType; +import org.apache.plc4x.java.spi.values.PlcRawByteArray; +import org.apache.plc4x.java.spi.values.PlcUSINT; +import org.apache.plc4x.java.spi.values.PlcValues; +import org.connectorio.addons.binding.canbus.CanConnection; + +public class DefaultCanConnection implements CanConnection { + + private final PlcConnection connection; + + public DefaultCanConnection(PlcConnection connection) { + this.connection = connection; + } + + @Override + public CompletableFuture write(int cob, byte[] data) { + GenericCANTag tag = new GenericCANTag(cob, GenericCANDataType.UNSIGNED8, 8); + return connection.writeRequestBuilder() + .addTag("data", tag, encode(data)) + .build().execute() + .thenApply(r -> null); + } + + private PlcValue encode(byte[] data) { + return new PlcRawByteArray(data); + } + + @Override + public Future subscribe(int cob, Consumer callback) { + GenericCANTag tag = new GenericCANTag(cob, GenericCANDataType.UNSIGNED8, 8); + + final AtomicReference registration = new AtomicReference<>(); + CompletableFuture ret = new CompletableFuture<>() { + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + PlcConsumerRegistration reg = registration.get(); + if (reg != null) { + reg.unregister(); + } + return super.cancel(mayInterruptIfRunning); + } + }; + + connection.subscriptionRequestBuilder() + .addEventTag("data", tag) + .build().execute().thenApply(r -> { + PlcConsumerRegistration reg = r.getSubscriptionHandle("data") + .register(new Consumer() { + @Override + public void accept(PlcSubscriptionEvent plcSubscriptionEvent) { + Collection data = plcSubscriptionEvent.getAllShorts("data"); + callback.accept(decode(new ArrayList<>(data))); + } + }); + registration.set(reg); + return registration; + }); + + return ret; + } + + private byte[] decode(List data) { + byte[] bytes = new byte[data.size()]; + for (int index = 0; index < bytes.length; index++) { + bytes[index] = data.get(index).byteValue(); + } + return bytes; + } +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/discovery/CANInterfaceDiscoveryDelegate.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/discovery/CANInterfaceDiscoveryDelegate.java new file mode 100644 index 000000000..93ba825d7 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/discovery/CANInterfaceDiscoveryDelegate.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.binding.canbus.internal.discovery; + +import java.util.Collections; +import java.util.Set; +import org.connectorio.addons.binding.canbus.CANbusBindingConstants; +import org.connectorio.addons.network.Network; +import org.connectorio.addons.network.NetworkType; +import org.connectorio.addons.network.can.CanNetwork; +import org.connectorio.addons.network.can.CanNetworkInterfaceTypes; +import org.connectorio.addons.network.can.CanNetworkTypes; +import org.connectorio.addons.network.iface.NetworkInterface; +import org.connectorio.addons.network.iface.NetworkInterfaceRegistry; +import org.connectorio.addons.network.iface.NetworkInterfaceStateCallback; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * Basic discovery service which rely on network API to discover CAN interface. + */ +@Component(service = {DiscoveryService.class, NetworkInterfaceStateCallback.class}, immediate = true) +public class CANInterfaceDiscoveryDelegate extends AbstractDiscoveryService implements DiscoveryService, + NetworkInterfaceStateCallback { + + private static final Set SUPPORTED_THING_TYPES = Collections.singleton( + CANbusBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE); + private final NetworkInterfaceRegistry networkInterfaceRegistry; + + @Activate + public CANInterfaceDiscoveryDelegate(@Reference NetworkInterfaceRegistry networkInterfaceRegistry) throws IllegalArgumentException { + super(SUPPORTED_THING_TYPES, 30, true); + this.networkInterfaceRegistry = networkInterfaceRegistry; + } + + @Override + protected void startScan() { + for (NetworkInterface networkInterface : networkInterfaceRegistry.getAll()) { + if (isCompatible(networkInterface)) { + discover(networkInterface); + } + } + } + + @Override + public void networkInterfaceUp(NetworkInterface networkInterface) { + if (isCompatible(networkInterface)) { + discover(networkInterface); + } + } + + @Override + public void networkInterfaceDown(NetworkInterface networkInterface) { + for (Network network : networkInterface.getNetworks()) { + if (!(network instanceof CanNetwork)) { + continue; + } + + // remove all discovery results which point to networks we just lost + thingRemoved(createThingUID(networkInterface)); + } + } + + private void discover(NetworkInterface networkInterface) { + for (Network network : networkInterface.getNetworks()) { + if (!(network instanceof CanNetwork)) { + continue; + } + + CanNetwork canNetwork = (CanNetwork) network; + NetworkType networkType = canNetwork.getType(); + if (CanNetworkTypes.CAN_RAW.equals(networkType)) { + String name = networkInterface.getUID().getName(); + final DiscoveryResult result = DiscoveryResultBuilder + .create(createThingUID(networkInterface)) + .withLabel("CAN interface " + name) + .withProperty("name", name) + .withRepresentationProperty("name") + .build(); + + thingDiscovered(result); + } + } + } + + private static ThingUID createThingUID(NetworkInterface networkInterface) { + return new ThingUID(CANbusBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE, networkInterface.getUID().getName()); + } + + private static boolean isCompatible(NetworkInterface networkInterface) { + return CanNetworkInterfaceTypes.SOCKETCAN.equals(networkInterface.getInterfaceType()) || + CanNetworkInterfaceTypes.VCAN.equals(networkInterface.getInterfaceType()); + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANMessageHandler.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANMessageHandler.java new file mode 100644 index 000000000..ff4d2fdef --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANMessageHandler.java @@ -0,0 +1,24 @@ +package org.connectorio.addons.binding.canbus.internal.handler; + +import org.connectorio.addons.binding.handler.GenericThingHandlerBase; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.Command; + +public class CANMessageHandler extends GenericThingHandlerBase { + + public CANMessageHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANThingHandlerFactory.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANThingHandlerFactory.java new file mode 100644 index 000000000..73ea0a32d --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/CANThingHandlerFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.binding.canbus.internal.handler; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.connectorio.addons.binding.canbus.CANbusBindingConstants; +import org.connectorio.addons.binding.canbus.discovery.CANDiscoveryParticipant; +import org.connectorio.addons.binding.handler.factory.BaseThingHandlerFactory; +import org.connectorio.plc4x.extras.osgi.PlcDriverManager; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; + +@Component(service = ThingHandlerFactory.class) +public class CANThingHandlerFactory extends BaseThingHandlerFactory implements ThingHandlerFactory { + + private final List participants = new CopyOnWriteArrayList<>(); + private final PlcDriverManager driverManager; + + @Activate + public CANThingHandlerFactory(@Reference PlcDriverManager driverManager) { + super(CANbusBindingConstants.SUPPORTED_THINGS); + this.driverManager = driverManager; + } + + @Override + protected ThingHandler createHandler(Thing thing) { + if (thing instanceof Bridge) { + if (CANbusBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE.equals(thing.getThingTypeUID())) { + return new SocketCANBridgeHandler((Bridge) thing, driverManager); + } + } + + if (CANbusBindingConstants.MESSAGE_THING_TYPE.equals(thing.getThingTypeUID())) { + return new CANMessageHandler(thing); + } + + return null; + } + + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + public void addDiscoveryParticipant(CANDiscoveryParticipant participant) { + participants.add(participant); + } + + public void removeDiscoveryParticipant(CANDiscoveryParticipant participant) { + participants.remove(participant); + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/SocketCANBridgeHandler.java b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/SocketCANBridgeHandler.java new file mode 100644 index 000000000..5e0a31011 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/java/org/connectorio/addons/binding/canbus/internal/handler/SocketCANBridgeHandler.java @@ -0,0 +1,57 @@ +package org.connectorio.addons.binding.canbus.internal.handler; + +import java.util.concurrent.CompletableFuture; +import org.apache.plc4x.java.api.PlcConnection; +import org.apache.plc4x.java.api.PlcDriver; +import org.connectorio.addons.binding.canbus.config.SocketCANConfiguration; +import org.connectorio.addons.binding.handler.GenericBridgeHandlerBase; +import org.connectorio.plc4x.extras.osgi.PlcDriverManager; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.types.Command; + +public class SocketCANBridgeHandler extends GenericBridgeHandlerBase { + + private final CompletableFuture connection = new CompletableFuture<>(); + private final PlcDriverManager driverManager; + + public SocketCANBridgeHandler(Bridge thing, PlcDriverManager driverManager) { + super(thing); + this.driverManager = driverManager; + } + + @Override + public void initialize() { + scheduler.execute(new Runnable() { + @Override + public void run() { + SocketCANConfiguration configuration = getBridgeConfig().get(); + + try { + PlcDriver driver = driverManager.getDriver("genericcan"); + PlcConnection plcConnection = driver.getConnection("genericcan:socketcan:" + configuration.name); + if (!plcConnection.isConnected()) { + plcConnection.connect(); + } + if (plcConnection.isConnected()) { + connection.complete(plcConnection); + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Could not open connection"); + } + } catch (Exception e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, + "Could not initialize handler " + e.getMessage()); + } + } + }); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + + } + +} diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 000000000..73a50b5fd --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,27 @@ + + + + + CAN bus Binding + This binding provides interaction layer for CAN enabled devices. + Ɓukasz Dywicki + + diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/channel-types.xml b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/channel-types.xml new file mode 100644 index 000000000..8951bf3a4 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/channel-types.xml @@ -0,0 +1,122 @@ + + + + + + Switch + + Switchable value which can be described using two values - on and off. + + + + Bit offset from beginning of data section (counted from 0). + + + + + Value which indicates 'ON' state, described using bit template. + + + + + Value which indicates 'OFF' state, described using bit template. + + + + + + Contact + + Contact which can be described using two values - open and closed. + + + + Bit offset from beginning of data section (counted from 0). + + + + Value which indicates 'OPEN' state, described using bit template. + + + + Value which indicates 'CLOSED' state, described using bit template. + + + + + + Number + + Numeric value interpreted according to typical binary encoding scheme. + + + + Bit offset from beginning of data section (counted from 0). + + + + Number of bits which should be read or written into message. + + + + Encoding of data and its endianness. + + + + + + + + + + + + + + + + + Switch little endian encoding for written/received values. + By default, all mappings are expected to be big endian. + + false + + + + + + Image + + Raw sequence of data, with no processing values are communicated using RawType (byte sequence). + + + + Bit offset from beginning of data section (counted from 0). + + + + Number of bits which should be read or written into message. + + + + + diff --git a/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 000000000..fc730e598 --- /dev/null +++ b/bundles/org.connectorio.addons.binding.canbus/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,66 @@ + + + + + + + Defines CAN interface which uses SocketCAN under the hood. + + + + + Name of interface assigned by operating system. + + + + + Time between poll cycles. + 1000 + ms + + + + + + + + + + + Mapping of a single CAN object identifier + + + + + Identifier of a message used to exchange information. + + + + + Acceptance filter for incoming message and/or patter for published message. + Please refer bit template documentation for detailed information. + + + + + + diff --git a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/internal/handler/protocol/TAOperations.java b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/internal/handler/protocol/TAOperations.java index 986e88fa9..4ecf1072e 100644 --- a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/internal/handler/protocol/TAOperations.java +++ b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/internal/handler/protocol/TAOperations.java @@ -48,6 +48,7 @@ import org.apache.plc4x.java.spi.generation.ParseException; import org.apache.plc4x.java.spi.generation.ReadBuffer; import org.apache.plc4x.java.spi.generation.ReadBufferByteBased; +import org.apache.plc4x.java.spi.values.PlcRawByteArray; import org.apache.plc4x.java.spi.values.PlcUSINT; import org.apache.plc4x.java.spi.values.PlcValues; import org.connectorio.addons.binding.canopen.ta.internal.type.TAString; @@ -144,31 +145,31 @@ public CompletableFuture reload(int nodeId) { } public CompletableFuture login(int nodeId, int clientId) { - return connection.writeRequestBuilder().addTag("mpdo", new CANOpenPDOTag(clientId, CANOpenService.RECEIVE_PDO_3, CANOpenDataType.RECORD), PlcValues.of( - new PlcUSINT((0x80 + nodeId)), - new PlcUSINT(0x00), - new PlcUSINT(0x1F), - new PlcUSINT(0x00), - new PlcUSINT(nodeId), - new PlcUSINT(clientId), - new PlcUSINT(0x80), - new PlcUSINT(0x12) - )).build().execute().whenComplete((response, error) -> { + return connection.writeRequestBuilder().addTag("mpdo", new CANOpenPDOTag(clientId, CANOpenService.RECEIVE_PDO_3, CANOpenDataType.RECORD), new PlcRawByteArray(new byte[] { + (byte) (0x80 + nodeId), + (byte) 0x00, + (byte) 0x1F, + (byte) 0x00, + (byte) nodeId, + (byte) clientId, + (byte) 0x80, + (byte) 0x12 + })).build().execute().whenComplete((response, error) -> { logger.debug("Dispatched login request to node {}", nodeId, error); }); } public CompletableFuture logout(int nodeId, int clientId) { - return connection.writeRequestBuilder().addTagAddress("mpdo", "RECEIVE_PDO_3:" + clientId + ":RECORD", PlcValues.of( - new PlcUSINT((0x80 + nodeId)), - new PlcUSINT(0x01), - new PlcUSINT(0x1F), - new PlcUSINT(0x00), - new PlcUSINT(nodeId), - new PlcUSINT(clientId), - new PlcUSINT(0x80), - new PlcUSINT(0x12) - )).build().execute().whenComplete((response, error) -> { + return connection.writeRequestBuilder().addTagAddress("mpdo", "RECEIVE_PDO_3:" + clientId + ":RECORD", new PlcRawByteArray(new byte[]{ + (byte) (0x80 + nodeId), + (byte) 0x01, + (byte) 0x1F, + (byte) 0x00, + (byte) nodeId, + (byte) clientId, + (byte) 0x80, + (byte) 0x12 + })).build().execute().whenComplete((response, error) -> { logger.debug("Dispatched logout request to node {}", nodeId, error); }); } @@ -247,8 +248,8 @@ public void accept(PlcSubscriptionEvent event) { IndexAddress address = IndexAddress.staticParse(buffer); final int subIndex = address.getSubindex(); short rawValue = buffer.readShort(16); - buffer.readUnsignedByte(8); // constant 0x41 - int unit = buffer.readUnsignedShort(8); + buffer.readUnsignedShort(8); // constant 0x41 + int unit = buffer.readShort(8); TAValue value = new TAValue(unit, rawValue); if (logger.isDebugEnabled()) { diff --git a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/ConfigurationCallback.java b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/ConfigurationCallback.java index 3317b4d4c..8c6f4b365 100644 --- a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/ConfigurationCallback.java +++ b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/ConfigurationCallback.java @@ -44,7 +44,7 @@ protected void accept(ReadBuffer buffer) throws ParseException { IndexAddress address = IndexAddress.staticParse(buffer); final int subIndex = address.getSubindex(); short rawValue = buffer.readShort(16); - buffer.readUnsignedByte(8); // constant 0x41 + short constant = buffer.readUnsignedShort(8); // constant 0x41 int unit = buffer.readUnsignedShort(8); if (logger.isDebugEnabled()) { diff --git a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/TADevice.java b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/TADevice.java index 86b3566a0..7939174d4 100644 --- a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/TADevice.java +++ b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/TADevice.java @@ -31,6 +31,7 @@ import java.util.function.Function; import org.apache.commons.codec.binary.Hex; import org.apache.plc4x.java.canopen.readwrite.CANOpenService; +import org.apache.plc4x.java.spi.values.PlcRawByteArray; import org.apache.plc4x.java.spi.values.PlcUSINT; import org.apache.plc4x.java.spi.values.PlcValues; import org.connectorio.addons.binding.canopen.api.CoNode; @@ -249,29 +250,29 @@ public void reload() { } public void login() { - node.getConnection().send(clientId, CANOpenService.RECEIVE_PDO_3, PlcValues.of( - new PlcUSINT(0x80 + node.getNodeId()), - new PlcUSINT(0x00), - new PlcUSINT(0x1F), - new PlcUSINT(0x00), - new PlcUSINT(node.getNodeId()), - new PlcUSINT(clientId), - new PlcUSINT(0x80), - new PlcUSINT(0x12) - )); + node.getConnection().send(clientId, CANOpenService.RECEIVE_PDO_3, new PlcRawByteArray(new byte[] { + (byte) (0x80 + node.getNodeId()), + (byte) 0x00, + (byte) 0x1F, + (byte) 0x00, + (byte) node.getNodeId(), + (byte) clientId, + (byte) 0x80, + (byte) 0x12 + })); } public void logout() { - node.getConnection().send(clientId, CANOpenService.RECEIVE_PDO_3, PlcValues.of( - new PlcUSINT(0x80 + node.getNodeId()), - new PlcUSINT(0x01), - new PlcUSINT(0x1F), - new PlcUSINT(0x00), - new PlcUSINT(node.getNodeId()), - new PlcUSINT(clientId), - new PlcUSINT(0x80), - new PlcUSINT(0x12) - )); + node.getConnection().send(clientId, CANOpenService.RECEIVE_PDO_3, new PlcRawByteArray(new byte[]{ + (byte) (0x80 + node.getNodeId()), + (byte) 0x01, + (byte) 0x1F, + (byte) 0x00, + (byte) node.getNodeId(), + (byte) clientId, + (byte) 0x80, + (byte) 0x12 + })); } public void close() { @@ -423,16 +424,16 @@ protected void subscribe(int nodeId, CANOpenService service, Consumer co private void reloadOutputs() { logger.info("Reload {} outputs.", node.getNodeId()); - node.getConnection().send(0, CANOpenService.TRANSMIT_PDO_4, PlcValues.of( - new PlcUSINT(0x80 + node.getNodeId()), - new PlcUSINT(0x01), - new PlcUSINT(0x4e), - new PlcUSINT(0x01), - new PlcUSINT(0x01), - new PlcUSINT(0x00), - new PlcUSINT(0x00), - new PlcUSINT(0x00) - )); + node.getConnection().send(0, CANOpenService.TRANSMIT_PDO_4, new PlcRawByteArray(new byte[]{ + (byte) (0x80 + node.getNodeId()), + (byte) 0x01, + (byte) 0x4e, + (byte) 0x01, + (byte) 0x01, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + })); } private void scanFunctions() { diff --git a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/publisher/PublishingRunnable.java b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/publisher/PublishingRunnable.java index a51c89060..953f8b08b 100644 --- a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/publisher/PublishingRunnable.java +++ b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/dev/publisher/PublishingRunnable.java @@ -22,6 +22,7 @@ import org.apache.plc4x.java.canopen.readwrite.CANOpenService; import org.apache.plc4x.java.spi.generation.WriteBuffer; import org.apache.plc4x.java.spi.generation.WriteBufferByteBased; +import org.apache.plc4x.java.spi.values.PlcRawByteArray; import org.apache.plc4x.java.spi.values.PlcSINT; import org.apache.plc4x.java.spi.values.PlcValues; import org.connectorio.addons.binding.canopen.api.CoNode; @@ -40,10 +41,7 @@ protected PublishingRunnable(CoNode node) { protected final void send(int nodeId, CANOpenService service, WriteBufferByteBased buffer) { byte[] data = buffer.getBytes(); logger.trace("Send to node {} {} (cob {}) data: {}", nodeId, service, Integer.toHexString(service.getMin() + nodeId), Hex.encodeHexString(data)); - node.getConnection().send(nodeId, service, PlcValues.of( - new PlcSINT(data[0]), new PlcSINT(data[1]), new PlcSINT(data[2]), new PlcSINT(data[3]), - new PlcSINT(data[4]), new PlcSINT(data[5]), new PlcSINT(data[6]), new PlcSINT(data[7]) - )); + node.getConnection().send(nodeId, service, new PlcRawByteArray(data)); } protected boolean hasKeyInRange(Map elements, int start, int limit) { diff --git a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/io/TAAnalogOutput.java b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/io/TAAnalogOutput.java index 64e150637..0d8e84ada 100644 --- a/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/io/TAAnalogOutput.java +++ b/bundles/org.connectorio.addons.binding.canopen.ta/src/main/java/org/connectorio/addons/binding/canopen/ta/tapi/io/TAAnalogOutput.java @@ -75,8 +75,8 @@ void update(byte[] data) { try { logger.trace("Encoded value for CAN Output {} is {}", this, Hex.encodeHexString(data)); ReadBuffer buffer = new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN); - this.type = buffer.readUnsignedByte(8); // type - this.unit = buffer.readUnsignedByte(8); // unit + this.type = buffer.readUnsignedShort(8); // type + this.unit = buffer.readUnsignedShort(8); // unit int numericValue = parseNumber(buffer, data.length); if (numericValue > Short.MIN_VALUE && numericValue < Short.MAX_VALUE) { diff --git a/bundles/org.connectorio.addons.binding.canopen/pom.xml b/bundles/org.connectorio.addons.binding.canopen/pom.xml index 06acb6cf0..838f63e8b 100644 --- a/bundles/org.connectorio.addons.binding.canopen/pom.xml +++ b/bundles/org.connectorio.addons.binding.canopen/pom.xml @@ -35,7 +35,7 @@ org.connectorio.addons - org.connectorio.addons.binding.can + org.connectorio.addons.network.can org.connectorio.addons @@ -45,16 +45,12 @@ org.connectorio.addons org.connectorio.units.si + org.apache.plc4x plc4j-driver-canopen - - org.connectorio.addons - org.connectorio.addons.binding.test - - org.openhab.core.bundles org.openhab.core.config.discovery @@ -73,14 +69,6 @@ org.connectorio.plc4x.extras.decorator.throttle - - org.connectorio.addons org.connectorio.addons.feature.plc4x @@ -88,6 +76,10 @@ xml + + org.connectorio.addons + org.connectorio.addons.binding.test + org.junit.jupiter junit-jupiter-api diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/discovery/CoInterfaceDiscoveryDelegate.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/discovery/CoInterfaceDiscoveryDelegate.java index c53a8c753..b5bf79972 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/discovery/CoInterfaceDiscoveryDelegate.java +++ b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/discovery/CoInterfaceDiscoveryDelegate.java @@ -19,49 +19,98 @@ import java.util.Collections; import java.util.Set; -import org.connectorio.addons.binding.can.CANInterface; -import org.connectorio.addons.binding.can.CANInterfaceTypes; -import org.connectorio.addons.binding.can.discovery.CANInterfaceDiscoveryDelegate; import org.connectorio.addons.binding.canopen.internal.CANopenBindingConstants; +import org.connectorio.addons.network.Network; +import org.connectorio.addons.network.NetworkType; +import org.connectorio.addons.network.can.CanNetwork; +import org.connectorio.addons.network.can.CanNetworkInterfaceTypes; +import org.connectorio.addons.network.can.CanNetworkTypes; +import org.connectorio.addons.network.iface.NetworkInterface; +import org.connectorio.addons.network.iface.NetworkInterfaceRegistry; +import org.connectorio.addons.network.iface.NetworkInterfaceStateCallback; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** - * Basic discovery service which rely on discovery of CAN interfaces. + * Basic discovery service which rely on network API to discover CAN interface. */ -@Component(service = {DiscoveryService.class, CANInterfaceDiscoveryDelegate.class}) +@Component(service = {DiscoveryService.class, NetworkInterfaceStateCallback.class}) public class CoInterfaceDiscoveryDelegate extends AbstractDiscoveryService implements DiscoveryService, - CANInterfaceDiscoveryDelegate { + NetworkInterfaceStateCallback { private static final Set SUPPORTED_THING_TYPES = Collections.singleton(CANopenBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE); + private final NetworkInterfaceRegistry networkInterfaceRegistry; - public CoInterfaceDiscoveryDelegate() throws IllegalArgumentException { + @Activate + public CoInterfaceDiscoveryDelegate(@Reference NetworkInterfaceRegistry networkInterfaceRegistry) throws IllegalArgumentException { super(SUPPORTED_THING_TYPES, 30, true); + this.networkInterfaceRegistry = networkInterfaceRegistry; } @Override - public void interfaceAvailable(CANInterface iface) { - if (CANInterfaceTypes.SOCKET_CAN.equals(iface.getType())) { - final String name = iface.getName(); - final DiscoveryResult result = DiscoveryResultBuilder - .create(new ThingUID(CANopenBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE, name)) - .withLabel("CANopen interface " + name) - .withProperty("name", name) - .withRepresentationProperty("name") - .build(); + protected void startScan() { + for (NetworkInterface networkInterface : networkInterfaceRegistry.getAll()) { + if (isCompatible(networkInterface)) { + discover(networkInterface); + } + } + } - thingDiscovered(result); + @Override + public void networkInterfaceUp(NetworkInterface networkInterface) { + if (isCompatible(networkInterface)) { + discover(networkInterface); } } @Override - protected void startScan() { + public void networkInterfaceDown(NetworkInterface networkInterface) { + for (Network network : networkInterface.getNetworks()) { + if (!(network instanceof CanNetwork)) { + continue; + } + + // remove all discovery results which point to networks we just lost + thingRemoved(createThingUID(networkInterface)); + } + } + + private void discover(NetworkInterface networkInterface) { + for (Network network : networkInterface.getNetworks()) { + if (!(network instanceof CanNetwork)) { + continue; + } + + CanNetwork canNetwork = (CanNetwork) network; + NetworkType networkType = canNetwork.getType(); + if (CanNetworkTypes.CANOPEN.equals(networkType)) { + String name = networkInterface.getUID().getName(); + final DiscoveryResult result = DiscoveryResultBuilder + .create(createThingUID(networkInterface)) + .withLabel("CANopen interface " + name) + .withProperty("name", name) + .withRepresentationProperty("name") + .build(); + + thingDiscovered(result); + } + } + } + + private static ThingUID createThingUID(NetworkInterface networkInterface) { + return new ThingUID(CANopenBindingConstants.SOCKETCAN_BRIDGE_THING_TYPE, networkInterface.getUID().getName()); + } + private static boolean isCompatible(NetworkInterface networkInterface) { + return CanNetworkInterfaceTypes.SOCKETCAN.equals(networkInterface.getInterfaceType()) || + CanNetworkInterfaceTypes.VCAN.equals(networkInterface.getInterfaceType()); } } diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoPdoTransmitHandler.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoPdoTransmitHandler.java index 3a774a51b..b54fa78ed 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoPdoTransmitHandler.java +++ b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoPdoTransmitHandler.java @@ -29,6 +29,7 @@ import org.apache.plc4x.java.spi.generation.SerializationException; import org.apache.plc4x.java.spi.generation.WriteBuffer; import org.apache.plc4x.java.spi.generation.WriteBufferByteBased; +import org.apache.plc4x.java.spi.values.PlcRawByteArray; import org.apache.plc4x.java.spi.values.PlcSINT; import org.apache.plc4x.java.spi.values.PlcValues; import org.connectorio.addons.binding.canopen.api.CoNode; @@ -104,16 +105,7 @@ private void publish() { } private PlcValue toPlcValue(byte[] data) { - return PlcValues.of( - new PlcSINT(data.length > 0 ? data[0] : 0), - new PlcSINT(data.length > 1 ? data[1] : 0), - new PlcSINT(data.length > 2 ? data[2] : 0), - new PlcSINT(data.length > 3 ? data[3] : 0), - new PlcSINT(data.length > 4 ? data[4] : 0), - new PlcSINT(data.length > 5 ? data[5] : 0), - new PlcSINT(data.length > 6 ? data[6] : 0), - new PlcSINT(data.length > 7 ? data[7] : 0) - ); + return new PlcRawByteArray(data); } private void writeState(WriteBuffer buffer, CANOpenDataType type, Command command) throws SerializationException { diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoSocketCANBridgeHandler.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoSocketCANBridgeHandler.java index 74a70e69f..920fc3b8f 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoSocketCANBridgeHandler.java +++ b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/handler/CoSocketCANBridgeHandler.java @@ -22,14 +22,11 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import javax.measure.Quantity; import org.apache.plc4x.java.api.PlcConnection; import org.apache.plc4x.java.api.exceptions.PlcConnectionException; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.canopen.tag.CANOpenTag; import org.apache.plc4x.java.spi.connection.AbstractPlcConnection; -import org.connectorio.addons.binding.can.statistic.CANStatisticCollector; import org.connectorio.addons.binding.canopen.api.CoConnection; import org.connectorio.addons.binding.canopen.discovery.CoDiscoveryParticipant; import org.connectorio.addons.binding.canopen.handler.CoBridgeHandler; @@ -37,16 +34,12 @@ import org.connectorio.addons.binding.canopen.internal.config.SocketCANConfiguration; import org.connectorio.addons.binding.canopen.internal.discovery.CoNetworkDiscoveryService; import org.connectorio.addons.binding.canopen.internal.plc4x.DefaultConnection; -import org.connectorio.addons.binding.canopen.internal.statistics.SocketCANStatisticCollectors; import org.connectorio.addons.binding.plc4x.handler.base.PollingPlc4xBridgeHandler; -import org.connectorio.plc4x.extras.osgi.PlcDriverManager; import org.connectorio.plc4x.extras.decorator.CompositeDecorator; import org.connectorio.plc4x.extras.decorator.Decorator; import org.connectorio.plc4x.extras.decorator.retry.RetryDecorator; import org.connectorio.plc4x.extras.decorator.throttle.ThrottleDecorator; -import org.openhab.core.library.types.DecimalType; -import org.openhab.core.library.types.QuantityType; -import org.openhab.core.library.unit.Units; +import org.connectorio.plc4x.extras.osgi.PlcDriverManager; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; @@ -61,7 +54,6 @@ public class CoSocketCANBridgeHandler extends PollingPlc4xBridgeHandler collectors; private CompletableFuture initializer = new CompletableFuture<>(); private List participants; private SocketCANConfiguration config; @@ -84,19 +76,26 @@ public void run() { config = getBridgeConfig().get(); String nodeId = "nodeId=" + config.nodeId; String heartbeat = "&heartbeat=" + config.heartbeat; - AbstractPlcConnection connection = (AbstractPlcConnection) driverManager - .getConnectionManager().getConnection("canopen:socketcan://" + config.name + "?" + nodeId + heartbeat); - if (connection.isConnected()) { + AbstractPlcConnection connection = null; + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); +// List> transports = ServiceLoader.load(Transport.class, getClass().getClassLoader()).stream().collect(Collectors.toList()); +// transports = ServiceLoader.load(Transport.class, SocketCANTransport.class.getClassLoader()).stream().collect(Collectors.toList()); + connection = (AbstractPlcConnection) driverManager.getConnectionManager().getConnection("canopen:socketcan://" + config.name + "?" + nodeId + heartbeat); + } finally { + Thread.currentThread().setContextClassLoader(tccl); + } + + if (connection != null && !connection.isConnected()) { + // force connection attempt + connection.connect(); + } + + if (connection != null && connection.isConnected()) { updateStatus(ThingStatus.ONLINE); initializer.complete(connection); - collectors = SocketCANStatisticCollectors.create(config.name); - statisticPoller = scheduler.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - fetchStatistics(); - } - }, 0, 1, TimeUnit.MINUTES); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection failed"); initializer.complete(null); @@ -115,26 +114,20 @@ public void run() { scheduler.submit(connectionTask); } - private void fetchStatistics() { - if (collectors == null || collectors.isEmpty() || getCallback() == null) { - return; - } - - for (CANStatisticCollector collector : collectors) { - Quantity statistic = collector.getStatistic(); - if (statistic.getUnit().equals(Units.ONE)) { - getCallback().stateUpdated(new ChannelUID(getThing().getUID(), collector.getName()), new DecimalType(statistic.getValue().doubleValue())); - } else { - getCallback().stateUpdated(new ChannelUID(getThing().getUID(), collector.getName()), new QuantityType<>(statistic.getValue().doubleValue(), statistic.getUnit())); - } - } - } - @Override public void dispose() { if (statisticPoller != null) { statisticPoller.cancel(true); } + initializer.thenAccept(connection -> { + if (connection.isConnected()) { + try { + connection.close(); + } catch (Exception e) { + logger.warn("Failure while closing connection", e); + } + } + }); } @Override diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/plc4x/CoRecordReader.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/plc4x/CoRecordReader.java index 3db5854d4..ca9ad3c73 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/plc4x/CoRecordReader.java +++ b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/plc4x/CoRecordReader.java @@ -33,13 +33,11 @@ public CoRecordReader(String field) { @Override protected byte[] extract(PlcReadResponse response, String field) { - List bytes = new ArrayList<>(response.getAllBytes(field)); - byte[] value = new byte[bytes.size()]; - for (int index = 0; index < bytes.size(); index++) { - value[index] = bytes.get(index); + Object bytes = response.getObject(field); + if (bytes instanceof byte[]) { + return (byte[]) bytes; } - - return value; + throw new IllegalArgumentException("Unexpected value, expected byte[] got " + (bytes != null ? bytes.getClass().getName() : "NULL")); } } diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollector.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollector.java deleted file mode 100644 index 414b0f6e1..000000000 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.canopen.internal.statistics; - -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; -import javax.measure.Quantity; -import javax.measure.quantity.Dimensionless; -import tec.uom.se.AbstractUnit; -import tec.uom.se.quantity.Quantities; - -/** - * Ratio calculation. - * - * Calculates number of exchanges between sampling periods. - */ -class RatioStatisticCollector extends SysfsStatisticCollector { - - private final Supplier clock; - private Long timestamp; - private Quantity value; - - public RatioStatisticCollector(Supplier clock, String iface, String name) { - super(iface, name, AbstractUnit.ONE); - this.clock = clock; - } - - @Override - public String getName() { - return super.getName() + "_ratio"; - } - - @Override - public Quantity getStatistic() { - long time = clock.get(); - Quantity count = readStatistic(); - - try { - if (timestamp == null) { - return Quantities.getQuantity(0.0, AbstractUnit.ONE); - } - - long period = time - timestamp; - Quantity difference = count.subtract(value); - - // twist bytes into bits and then divide by seconds between samples - double ratio = difference.getValue().doubleValue() / (int) TimeUnit.MILLISECONDS.toSeconds(period); - return Quantities.getQuantity(ratio, AbstractUnit.ONE); - } finally { - mark(count, time); - } - } - - private void mark(Quantity count, long time) { - value = count; - timestamp = time; - } - -} diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SocketCANStatisticCollectors.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SocketCANStatisticCollectors.java deleted file mode 100644 index c6d609f07..000000000 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SocketCANStatisticCollectors.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.canopen.internal.statistics; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import org.connectorio.addons.binding.can.statistic.CANStatisticCollector; -import org.openhab.core.library.unit.Units; - -/** - * Creation of socketcan specific statistic collectors. - */ -public abstract class SocketCANStatisticCollectors { - - public static List create(String iface) { - List collectors = new ArrayList<>(); - File statistics = new File("/sys/class/net/" + iface + "/statistics"); - - if (statistics.isDirectory() && statistics.canRead()) { - collectors.add(new SysfsStatisticCollector<>(iface, "rx_bytes", Units.BYTE)); - collectors.add(new SysfsStatisticCollector<>(iface, "tx_bytes", Units.BYTE)); - collectors.add(new SysfsStatisticCollector<>(iface, "rx_packets", Units.ONE)); - collectors.add(new SysfsStatisticCollector<>(iface, "tx_packets", Units.ONE)); - - collectors.add(new BandwidthStatisticCollector(System::currentTimeMillis, iface, "rx_bytes")); - collectors.add(new BandwidthStatisticCollector(System::currentTimeMillis, iface, "tx_bytes")); - collectors.add(new RatioStatisticCollector(System::currentTimeMillis, iface, "rx_packets")); - collectors.add(new RatioStatisticCollector(System::currentTimeMillis, iface, "tx_packets")); - } - return collectors; - } - -} diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsReader.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsReader.java deleted file mode 100644 index 882fc0d06..000000000 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsReader.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.canopen.internal.statistics; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; -import javax.measure.Quantity; -import javax.measure.Unit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tec.uom.se.quantity.Quantities; - -public class SysfsReader> { - - private final Logger logger = LoggerFactory.getLogger(SysfsReader.class); - private final String iface; - private final String name; - private final Unit unit; - - public SysfsReader(String iface, String name, Unit unit) { - this.iface = iface; - this.name = name; - this.unit = unit; - } - - public String getName() { - return name; - } - - protected Quantity readStatistic() { - File statistic = new File("/sys/class/net/" + iface + "/statistics/" + name); - - if (statistic.isFile() && statistic.canRead()) { - try { - List readings = Files.readAllLines(statistic.toPath()); - if (readings.size() == 1 && !readings.get(0).isEmpty()) { - return Quantities.getQuantity(Long.parseLong(readings.get(0)), unit); - } else { - logger.debug("Can not map readings {} for statistic {}.", readings, statistic.getName()); - } - } catch (IOException e) { - logger.debug("Field to read can interface statistics.", e); - } - } - return Quantities.getQuantity(0, unit); - } - -} diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsStatisticCollector.java b/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsStatisticCollector.java deleted file mode 100644 index 1829deee1..000000000 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/SysfsStatisticCollector.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.connectorio.addons.binding.canopen.internal.statistics; - -import javax.measure.Quantity; -import javax.measure.Unit; -import org.connectorio.addons.binding.can.statistic.CANStatisticCollector; - -/** - * Statistic collector which reads linux /sys/class/net filesystem in pursue of CAN interface statistics. - * - * Below logic is very simple and can be in fact adopted to any network interface. - */ -class SysfsStatisticCollector> extends SysfsReader implements CANStatisticCollector { - - public SysfsStatisticCollector(String iface, String name, Unit unit) { - super(iface, name, unit); - } - - @Override - public Quantity getStatistic() { - return readStatistic(); - } - -} diff --git a/bundles/org.connectorio.addons.binding.can/pom.xml b/bundles/org.connectorio.addons.network.can.dbus/pom.xml similarity index 76% rename from bundles/org.connectorio.addons.binding.can/pom.xml rename to bundles/org.connectorio.addons.network.can.dbus/pom.xml index cab5efd8d..f36e479b6 100644 --- a/bundles/org.connectorio.addons.binding.can/pom.xml +++ b/bundles/org.connectorio.addons.network.can.dbus/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + + org.connectorio.addons + bundles + 3.0.0-SNAPSHOT + + + org.connectorio.addons.network.can + bundle + + ConnectorIO - Addons - Network - CAN + CAN bus related network API. + + + + org.connectorio.addons + org.connectorio.addons.network + ${project.version} + + + + diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/LifecycleListener.java b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetwork.java similarity index 72% rename from bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/LifecycleListener.java rename to bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetwork.java index 282d3cc7b..6d8372d77 100644 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/LifecycleListener.java +++ b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetwork.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,12 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.can; +package org.connectorio.addons.network.can; -@Deprecated -public interface LifecycleListener { +import org.connectorio.addons.network.Network; - void interfaceStarted(CANInterface iface); +public interface CanNetwork extends Network { - void interfaceStopped(CANInterface iface); + // maybe baud rate? } diff --git a/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterface.java b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterface.java new file mode 100644 index 000000000..9295c25fa --- /dev/null +++ b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterface.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.can; + +import org.connectorio.addons.network.iface.NetworkInterface; + +public interface CanNetworkInterface extends NetworkInterface { + + String getName(); + + String getDisplayName(); + + String getDriver(); + + boolean isUp(); + +} diff --git a/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterfaceTypes.java b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterfaceTypes.java new file mode 100644 index 000000000..0f75dbf9f --- /dev/null +++ b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkInterfaceTypes.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.can; + +import org.connectorio.addons.network.iface.NetworkInterfaceType; + +public class CanNetworkInterfaceTypes { + public static final NetworkInterfaceType SOCKETCAN = new NetworkInterfaceType() { + @Override + public String getType() { + return "socketcan"; + } + }; + + public static final NetworkInterfaceType VCAN = new NetworkInterfaceType() { + @Override + public String getType() { + return "vcan"; + } + }; + +} diff --git a/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkTypes.java b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkTypes.java new file mode 100644 index 000000000..a18d0fd22 --- /dev/null +++ b/bundles/org.connectorio.addons.network.can/src/main/java/org/connectorio/addons/network/can/CanNetworkTypes.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.can; + +import org.connectorio.addons.network.NetworkType; + +public class CanNetworkTypes { + + public final static NetworkType CANOPEN = new NetworkType() { + @Override + public String getType() { + return "canopen"; + } + }; + + public final static NetworkType CAN_RAW = new NetworkType() { + @Override + public String getType() { + return "can-raw"; + } + }; + +} diff --git a/bundles/org.connectorio.addons.network.jvm/pom.xml b/bundles/org.connectorio.addons.network.jvm/pom.xml index 6a734e387..efc2add11 100644 --- a/bundles/org.connectorio.addons.network.jvm/pom.xml +++ b/bundles/org.connectorio.addons.network.jvm/pom.xml @@ -36,7 +36,6 @@ org.connectorio.addons org.connectorio.addons.network.ip - ${project.version} diff --git a/bundles/org.connectorio.addons.network.linux/pom.xml b/bundles/org.connectorio.addons.network.linux/pom.xml new file mode 100644 index 000000000..df51be27b --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + + org.connectorio.addons + bundles + 3.0.0-SNAPSHOT + + + org.connectorio.addons.network.linux + bundle + + ConnectorIO - Addons - Network - Linux + Plain network support based on Linux. + + + + org.connectorio.addons + org.connectorio.addons.network + + + + org.osgi + org.osgi.service.component.annotations + + + + org.junit.jupiter + junit-jupiter-api + + + org.mockito + mockito-junit-jupiter + + + org.assertj + assertj-core + + + + diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplier.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplier.java new file mode 100644 index 000000000..41a55834f --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplier.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.measure.Quantity; +import org.connectorio.addons.network.iface.NamedMetricCode; +import org.connectorio.addons.network.iface.MetricsSupplier; +import org.connectorio.addons.network.iface.MetricCode; +import org.connectorio.addons.network.iface.NetworkInterfaceUID; +import org.connectorio.addons.network.iface.linux.internal.sysfs.LinuxSysfsStatisticsCollector; +import org.connectorio.addons.network.iface.linux.internal.sysfs.PlainSysfsReader; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component +public class LinuxNetworkMetricsSupplier implements MetricsSupplier { + + private final Logger logger = LoggerFactory.getLogger(LinuxNetworkMetricsSupplier.class); + private final Map>> statistics = new ConcurrentHashMap<>(); + private final SysfsReader reader; + private final ScheduledExecutorService executor; + private final long sampleTimeMs; + + @Activate + public LinuxNetworkMetricsSupplier(@Reference SysfsReader reader) { + this(reader.narrow("class", "net"), Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread(runnable, "linux-nic-stat-collector"); + thread.setDaemon(true); + return thread; + }), 60_000); + } + + public LinuxNetworkMetricsSupplier(SysfsReader reader, ScheduledExecutorService executor, long sampleTimeMs) { + this.reader = reader; + this.executor = executor; + this.sampleTimeMs = sampleTimeMs; + + executor.scheduleAtFixedRate(this::fetch, 0, sampleTimeMs, TimeUnit.MILLISECONDS); + } + + void fetch() { + Set registeredInterfaces = new HashSet<>(statistics.keySet()); + for (String nif : reader.list()) { + LinuxSysfsStatisticsCollector collector = new LinuxSysfsStatisticsCollector(reader); + Map> stats = collector.collect(nif); + Map> interfaceMetrics = new LinkedHashMap<>(); + for (Map.Entry> entry : stats.entrySet()) { + interfaceMetrics.put(new NamedMetricCode(entry.getKey()), entry.getValue()); + } + // flush stats into statistics map + statistics.put(nif, interfaceMetrics); + registeredInterfaces.remove(nif); + } + + if (!registeredInterfaces.isEmpty()) { + logger.info("Detected removal of network interfaces: {}. Cleaning up resources.", registeredInterfaces); + for (String nic : registeredInterfaces) { + statistics.remove(nic); + } + } + } + + @Deactivate + public void shutdown() { + executor.shutdownNow(); + } + + @Override + public Map> getMetrics(NetworkInterfaceUID interfaceId) { + String iface = interfaceId.getName(); + if (!statistics.containsKey(iface)) { + return Collections.emptyMap(); + } + + return statistics.get(iface); + } + + public static void main(String[] args) throws Exception { + long sampleTimeMs = 10_000; + LinuxNetworkMetricsSupplier supplier = new LinuxNetworkMetricsSupplier(new PlainSysfsReader().narrow("class", "net"), + Executors.newSingleThreadScheduledExecutor(), sampleTimeMs + ); + + while (true) { + try { + Thread.sleep(sampleTimeMs); + } catch (InterruptedException e) { + break; + } + + long time = System.currentTimeMillis(); + System.out.println( time + "\n---"); + Map> metrics = supplier.getMetrics(new NetworkInterfaceUID("eth", "ens2f1")); + + for (Entry> entry : metrics.entrySet()) { + MetricCode k = entry.getKey(); + Quantity v = entry.getValue(); + if (v.getValue().intValue() != 0 || k.getCode().contains("bandwidth")) { + System.out.println(k.getCode() + "=" + v); + } + } + System.out.println("---"); + } + + System.in.read(); + } +} diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/statistic/CANStatisticCollector.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticCollector.java similarity index 73% rename from bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/statistic/CANStatisticCollector.java rename to bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticCollector.java index 57000cd30..636c2eb16 100644 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/statistic/CANStatisticCollector.java +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticCollector.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +15,14 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.can.statistic; +package org.connectorio.addons.network.iface.linux.internal; import javax.measure.Quantity; /** - * Helper interface to gather CAN statistics. + * Statistic collector which to pursue network interface statistics. */ -@Deprecated -public interface CANStatisticCollector> { - - String getName(); +public interface StatisticCollector> { Quantity getStatistic(); diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticProcessor.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticProcessor.java new file mode 100644 index 000000000..5896b150e --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticProcessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal; + +import java.util.Map; +import java.util.function.Supplier; +import javax.measure.Quantity; + +public interface StatisticProcessor { + + Quantity process(Supplier clock, Map> stats); + +} diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticsCollector.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticsCollector.java new file mode 100644 index 000000000..4e651f997 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/StatisticsCollector.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal; + +import java.util.Map; +import javax.measure.Quantity; + +/** + * Collects all available statistics for network interface based on interface name. + */ +public interface StatisticsCollector { + + Map> collect(String nif); + +} diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANThingHandler.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/SysfsReader.java similarity index 68% rename from bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANThingHandler.java rename to bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/SysfsReader.java index 35703afce..738561cc9 100644 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANThingHandler.java +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/SysfsReader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,17 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.can; +package org.connectorio.addons.network.iface.linux.internal; -import java.util.Optional; -import org.openhab.core.thing.binding.BridgeHandler; +import java.util.List; +import java.util.Set; -@Deprecated -public interface CANThingHandler extends BridgeHandler { +public interface SysfsReader { - Optional getName(); + List read(String file); + + Set list(); + + SysfsReader narrow(String ... path); } diff --git a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollector.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessor.java similarity index 62% rename from bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollector.java rename to bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessor.java index 0e7ecc538..a21e2cad5 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/main/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollector.java +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,14 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.canopen.internal.statistics; +package org.connectorio.addons.network.iface.linux.internal.sysfs; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import javax.measure.IncommensurableException; import javax.measure.Quantity; -import org.connectorio.addons.binding.can.statistic.CANStatisticCollector; +import org.connectorio.addons.network.iface.linux.internal.StatisticProcessor; import org.openhab.core.library.dimension.DataAmount; import org.openhab.core.library.dimension.DataTransferRate; import org.openhab.core.library.unit.Units; @@ -31,29 +33,30 @@ * * Takes in byte statistic and returns amount of bits exchanged between sampling periods. */ -class BandwidthStatisticCollector extends SysfsReader implements CANStatisticCollector { +class BandwidthStatisticProcessor implements StatisticProcessor { - private final Supplier clock; + private final String baseStatistic; private Long timestamp; private Quantity value; - public BandwidthStatisticCollector(Supplier clock, String iface, String name) { - super(iface, name, Units.BYTE); - this.clock = clock; + BandwidthStatisticProcessor(String baseStatistic) { + this.baseStatistic = baseStatistic; } @Override - public String getName() { - return super.getName() + "_bandwidth"; - } - - @Override - public Quantity getStatistic() { + public Quantity process(Supplier clock, Map> stats) { long time = clock.get(); - Quantity count = readStatistic(); + Quantity quantity = stats.get(baseStatistic); + if (quantity == null) { + return null; + } + + Quantity count = null; try { - if (timestamp == null) { + count = Quantities.getQuantity(quantity.getUnit().getConverterToAny(Units.BIT).convert(quantity.getValue()), Units.BIT); + + if (timestamp == null || value == null) { return Quantities.getQuantity(0.0, Units.BIT_PER_SECOND); } @@ -63,9 +66,11 @@ public Quantity getStatistic() { // twist bytes into bits and then divide by seconds between samples long timeSpan = TimeUnit.MILLISECONDS.toSeconds(period); if (timeSpan > 0) { - return (Quantity) difference.divide(Quantities.getQuantity(timeSpan, Units.SECOND)); + return Quantities.getQuantity((difference.getValue().doubleValue() / timeSpan), Units.BIT_PER_SECOND); } return Quantities.getQuantity(0.0, Units.BIT_PER_SECOND); + } catch (IncommensurableException e) { + return null; } finally { mark(count, time); } diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/LinuxSysfsStatisticsCollector.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/LinuxSysfsStatisticsCollector.java new file mode 100644 index 000000000..4654b5692 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/LinuxSysfsStatisticsCollector.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal.sysfs; + +import static java.util.Map.entry; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Supplier; +import javax.measure.Quantity; +import javax.measure.Unit; +import org.connectorio.addons.network.iface.linux.internal.StatisticProcessor; +import org.connectorio.addons.network.iface.linux.internal.SysfsReader; +import org.connectorio.addons.network.iface.linux.internal.StatisticsCollector; +import org.openhab.core.library.unit.Units; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common linux network interface statistic collector - rely on sysfs entries. + + * Additionally, on top of collected data, this collector will create a derivatives: rx/tx bandwidth + * as well as rx/tx packets per second. + */ +public class LinuxSysfsStatisticsCollector implements StatisticsCollector { + + private final static Map> MAPPING = Map.ofEntries( + entry("rx_bytes", Units.BYTE), + entry("tx_bytes", Units.BYTE), + // dimensionless + entry("collisions", Units.ONE), + entry("multicast", Units.ONE), + entry("rx_compressed", Units.ONE), + entry("rx_crc_errors", Units.ONE), + entry("rx_dropped", Units.ONE), + entry("rx_errors", Units.ONE), + entry("rx_fifo_errors", Units.ONE), + entry("rx_frame_errors", Units.ONE), + entry("rx_length_errors", Units.ONE), + entry("rx_missed_errors", Units.ONE), + entry("rx_nohandler", Units.ONE), + entry("rx_over_errors", Units.ONE), + entry("rx_packets", Units.ONE), + entry("tx_aborted_errors", Units.ONE), + entry("tx_carrier_errors", Units.ONE), + entry("tx_compressed", Units.ONE), + entry("tx_dropped", Units.ONE), + entry("tx_errors", Units.ONE), + entry("tx_fifo_errors", Units.ONE), + entry("tx_heartbeat_errors", Units.ONE), + entry("tx_packets", Units.ONE), + entry("tx_window_errors", Units.ONE) + ); + + private final static Map PROCESSORS = Map.ofEntries( + entry("rx_bandwidth", new BandwidthStatisticProcessor("rx_bytes")), + entry("tx_bandwidth", new BandwidthStatisticProcessor("tx_bytes")), + entry("rx_packets_per_second", new RatioStatisticProcessor("rx_packets")), + entry("tx_packets_per_second", new RatioStatisticProcessor("tx_packets")) + ); + + private final Logger logger = LoggerFactory.getLogger(LinuxSysfsStatisticsCollector.class); + + private final Supplier clock; + private final SysfsReader reader; + + public LinuxSysfsStatisticsCollector(SysfsReader reader) { + this(System::currentTimeMillis, reader); + } + + public LinuxSysfsStatisticsCollector(Supplier clock, SysfsReader reader) { + this.clock = clock; + this.reader = reader; + } + + public Map> collect(String iface) { + SysfsReader statisticReader = reader.narrow(iface, "statistics"); + + Map> collected = new HashMap<>(); + Set stats = statisticReader.list(); + + logger.debug("Retrieving statistics {} of network interface {} through sysfs", stats, iface); + for (String stat : stats) { + Unit unit = MAPPING.getOrDefault(stat, Units.ONE); + SysfsStatisticCollector collector = new SysfsStatisticCollector<>(statisticReader, stat, unit); + Quantity statistic = collector.getStatistic(); + if (statistic != null) { + collected.put(stat, statistic); + } + } + + for (Entry entry : PROCESSORS.entrySet()) { + Quantity processed = entry.getValue().process(clock, collected); + if (processed != null) { + collected.put(entry.getKey(), processed); + } + } + + return collected; + } + +} diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/PlainSysfsReader.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/PlainSysfsReader.java new file mode 100644 index 000000000..00f330075 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/PlainSysfsReader.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal.sysfs; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import org.connectorio.addons.network.iface.linux.internal.SysfsReader; +import org.osgi.service.component.annotations.Component; + +@Component +public class PlainSysfsReader implements SysfsReader { + + private final File base; + + public PlainSysfsReader() { + this("/sys"); + } + + public PlainSysfsReader(String base) { + this(new File(base)); + } + + public PlainSysfsReader(File base) { + this.base = base; + } + + @Override + public List read(String file) { + File path = new File(base, file); + if (path.isFile() && path.canRead()) { + try { + return Files.readAllLines(path.toPath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return Collections.emptyList(); + } + + @Override + public Set list() { + if (base.isDirectory() && base.canRead()) { + Set filesOrDirectories = new LinkedHashSet<>(); + + String[] list = base.list(); + if (list == null) { + return filesOrDirectories; + } + + filesOrDirectories.addAll(Arrays.asList(list)); + + return filesOrDirectories; + } + return Collections.emptySet(); + } + + @Override + public SysfsReader narrow(String ... path) { + return new PlainSysfsReader(new File(base, Arrays.stream(path) + .reduce((upper, lower) -> upper + "/" + lower) + .orElse("") + )); + } + + +} diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticProcessor.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticProcessor.java new file mode 100644 index 000000000..e8c0bb706 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticProcessor.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal.sysfs; + +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import javax.measure.IncommensurableException; +import javax.measure.Quantity; +import javax.measure.Unit; +import javax.measure.quantity.Dimensionless; +import org.connectorio.addons.network.iface.linux.internal.StatisticProcessor; +import tec.uom.se.AbstractUnit; +import tec.uom.se.quantity.Quantities; +import tec.uom.se.unit.Units; + +/** + * Ratio calculation - calculates number of exchanges within sampling periods. + */ +@SuppressWarnings("rawtypes") +class RatioStatisticProcessor implements StatisticProcessor { + + private final String baseStatistic; + private Long timestamp; + private Quantity value; + + public RatioStatisticProcessor(String baseStatistic) { + this.baseStatistic = baseStatistic; + } + + @Override + public Quantity process(Supplier clock, Map> stats) { + long time = clock.get(); + Quantity quantity = stats.get(baseStatistic); + + if (quantity == null) { + return null; + } + + Quantity count = null; + try { + count = quantity; + Unit targetUnit = quantity.getUnit().divide(Units.SECOND); + + if (timestamp == null || value == null) { + return Quantities.getQuantity(0.0, targetUnit); + } + + long period = time - timestamp; + Quantity difference = count.subtract(value); + + // twist value into increased value and then divide by seconds between samples + double ratio = difference.getValue().doubleValue() / TimeUnit.MILLISECONDS.toSeconds(period); + return Quantities.getQuantity(ratio, targetUnit); + } finally { + mark(count, time); + } + } + + private void mark(Quantity count, long time) { + value = count; + timestamp = time; + } + +} diff --git a/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/SysfsStatisticCollector.java b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/SysfsStatisticCollector.java new file mode 100644 index 000000000..a78928a20 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/main/java/org/connectorio/addons/network/iface/linux/internal/sysfs/SysfsStatisticCollector.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal.sysfs; + +import java.util.List; +import javax.measure.Quantity; +import javax.measure.Unit; +import org.connectorio.addons.network.iface.linux.internal.StatisticCollector; +import org.connectorio.addons.network.iface.linux.internal.SysfsReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tec.uom.se.quantity.Quantities; + +public class SysfsStatisticCollector> implements StatisticCollector { + + private final Logger logger = LoggerFactory.getLogger(SysfsStatisticCollector.class); + private final SysfsReader reader; + private final String statistic; + private final Unit unit; + + public SysfsStatisticCollector(SysfsReader reader, String statistic, Unit unit) { + this.reader = reader; + this.statistic = statistic; + this.unit = unit; + } + + public Quantity getStatistic() { + try { + List readings = reader.read(statistic); + if (readings.size() == 1 && !readings.get(0).isEmpty()) { + return Quantities.getQuantity(Long.parseLong(readings.get(0)), unit); + } else { + logger.debug("Can not map contents of file {} to plain statistic: {}.", statistic, readings); + } + } catch (Exception e) { + logger.debug("Field to read network interface statistics through {}", reader, e); + } + + return null; + } + +} diff --git a/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplierTest.java b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplierTest.java new file mode 100644 index 000000000..29a5c188c --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/LinuxNetworkMetricsSupplierTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import java.util.List; +import java.util.Map; +import javax.measure.Quantity; +import org.connectorio.addons.network.iface.MetricCode; +import org.connectorio.addons.network.iface.NamedMetricCode; +import org.connectorio.addons.network.iface.NetworkInterfaceUID; +import org.connectorio.addons.network.iface.linux.internal.sysfs.MockSysfsReader; +import org.junit.jupiter.api.Test; + +public class LinuxNetworkMetricsSupplierTest { + + @Test + void testSupplier() { + MockSysfsReader reader = new MockSysfsReader(Map.of( + "/sys/class/net/eno0", List.of(), + "/sys/class/net/eno0/statistics", List.of(), + "/sys/class/net/eno0/statistics/tx_packets", List.of("5000"), + "/sys/class/net/eno0/statistics/tx_bytes", List.of("1000") + )); + + LinuxNetworkMetricsSupplier supplier = new LinuxNetworkMetricsSupplier(reader); + supplier.fetch(); + Map> metrics = supplier.getMetrics(new NetworkInterfaceUID("eth", "eno0")); + + assertThat(metrics).isNotNull() + .containsKey(new NamedMetricCode("tx_packets")) + .containsKey(new NamedMetricCode("tx_bandwidth")) + .containsKey(new NamedMetricCode("tx_packets_per_second")); + } + +} diff --git a/bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollectorTest.java b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessorTest.java similarity index 65% rename from bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollectorTest.java rename to bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessorTest.java index e9bc69b2d..a32d404b0 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/BandwidthStatisticCollectorTest.java +++ b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/BandwidthStatisticProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.canopen.internal.statistics; +package org.connectorio.addons.network.iface.linux.internal.sysfs; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -import java.util.concurrent.atomic.AtomicReference; +import java.util.HashMap; +import java.util.Map; import java.util.function.Supplier; import javax.measure.Quantity; import org.junit.jupiter.api.Test; @@ -29,36 +30,34 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.core.library.dimension.DataAmount; import org.openhab.core.library.dimension.DataTransferRate; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; +import tec.uom.se.ComparableQuantity; import tec.uom.se.quantity.Quantities; @ExtendWith(MockitoExtension.class) -class BandwidthStatisticCollectorTest { +class BandwidthStatisticProcessorTest { + public static final String BASE_STATISTIC = "tx_bytes"; @Mock Supplier clock; - AtomicReference> statistic = new AtomicReference(); @Test - void testRatioCalculation() { - BandwidthStatisticCollector collector = new BandwidthStatisticCollector(clock, "foo", "tx_bytes") { - @Override - protected Quantity readStatistic() { - return statistic.get(); - } - }; + void testBandwidthCalculation() { + Map> statistics = new HashMap<>(); + BandwidthStatisticProcessor processor = new BandwidthStatisticProcessor(BASE_STATISTIC); when(clock.get()).thenReturn(1000L); - statistic.set(Quantities.getQuantity(0, Units.BYTE)); - assertThat(collector.getStatistic()) + statistics.put(BASE_STATISTIC, Quantities.getQuantity(0, Units.BYTE)); + assertThat(processor.process(clock, statistics)) .extracting(Quantity::getValue) .extracting(Number::intValue) .isEqualTo(0); // one second and one byte later we expect 8 bit/s ratio. when(clock.get()).thenReturn(2000L); - statistic.set(Quantities.getQuantity(1, Units.BYTE)); - Quantity statistic = collector.getStatistic(); + statistics.put(BASE_STATISTIC, Quantities.getQuantity(1, Units.BYTE)); + Quantity statistic = processor.process(clock, statistics); assertThat(statistic) .extracting(Quantity::getValue) .extracting(Number::doubleValue) // escape from BigDecimal @@ -66,8 +65,9 @@ protected Quantity readStatistic() { // eight seconds and one byte later we expect 1 bit/s ratio. when(clock.get()).thenReturn(10000L); - this.statistic.set(Quantities.getQuantity(2, Units.BYTE)); - assertThat(collector.getStatistic()) + statistics.put(BASE_STATISTIC, Quantities.getQuantity(2, Units.BYTE)); + + assertThat(processor.process(clock, statistics)) .extracting(Quantity::getValue) .extracting(Number::doubleValue) .isEqualTo(1.0); diff --git a/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/MockSysfsReader.java b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/MockSysfsReader.java new file mode 100644 index 000000000..ccb864b32 --- /dev/null +++ b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/MockSysfsReader.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface.linux.internal.sysfs; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.connectorio.addons.network.iface.linux.internal.SysfsReader; + +public class MockSysfsReader implements SysfsReader { + + private final String path; + private final Map> contents; + + public MockSysfsReader(Map> contents) { + this("/sys", contents); + } + + public MockSysfsReader(String path, Map> contents) { + this.path = path; + this.contents = contents; + } + + @Override + public List read(String file) { + String key = path + "/" + file; + return contents.getOrDefault(key, Collections.emptyList()); + } + + @Override + public Set list() { + Set matching = new LinkedHashSet<>(); + for (String key : contents.keySet()) { + if (key.startsWith(path)) { + if (key.length() < path.length() + 1) { + continue; + } + + String entry = key.substring(path.length() + 1); + if (!entry.contains("/")) { + matching.add(entry); + } + } + } + return matching; + } + + @Override + public SysfsReader narrow(String ... path) { + return new MockSysfsReader(this.path + "/" + Arrays.stream(path) + .reduce((upper, lower) -> upper + "/" + lower) + .orElse(""), + contents + ); + } + + void update(String path, List content) { + contents.put(path, content); + } + +} diff --git a/bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollectorTest.java b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticCollectorTest.java similarity index 62% rename from bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollectorTest.java rename to bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticCollectorTest.java index f7eab5f85..d20d4fae3 100644 --- a/bundles/org.connectorio.addons.binding.canopen/src/test/java/org/connectorio/addons/binding/canopen/internal/statistics/RatioStatisticCollectorTest.java +++ b/bundles/org.connectorio.addons.network.linux/src/test/java/org/connectorio/addons/network/iface/linux/internal/sysfs/RatioStatisticCollectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2019-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,15 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.canopen.internal.statistics; +package org.connectorio.addons.network.iface.linux.internal.sysfs; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; -import java.util.concurrent.atomic.AtomicReference; +import java.util.HashMap; +import java.util.Map; import java.util.function.Supplier; import javax.measure.Quantity; -import javax.measure.quantity.Dimensionless; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -34,30 +34,27 @@ @ExtendWith(MockitoExtension.class) class RatioStatisticCollectorTest { + public static final String BASE_STATISTIC = "tx_packets"; @Mock Supplier clock; - AtomicReference> statistic = new AtomicReference<>(); @Test void testRatioCalculation() { - RatioStatisticCollector collector = new RatioStatisticCollector(clock, "foo", "tx_packets") { - @Override - protected Quantity readStatistic() { - return statistic.get(); - } - }; + Map> statistics = new HashMap<>(); + RatioStatisticProcessor collector = new RatioStatisticProcessor(BASE_STATISTIC); - when(clock.get()).thenReturn(1000L); - statistic.set(Quantities.getQuantity(1000, AbstractUnit.ONE)); - Quantity collectorStatistic = collector.getStatistic(); + when(clock.get()).thenReturn(0L); + statistics.put(BASE_STATISTIC, Quantities.getQuantity(0, AbstractUnit.ONE)); + + Quantity collectorStatistic = collector.process(clock, statistics); assertThat(collectorStatistic) .extracting(Quantity::getValue) .extracting(Number::doubleValue) .isEqualTo(0.0); - when(clock.get()).thenReturn(2000L); - this.statistic.set(Quantities.getQuantity(1001, AbstractUnit.ONE)); - collectorStatistic = collector.getStatistic(); + when(clock.get()).thenReturn(1000L); + statistics.put(BASE_STATISTIC, Quantities.getQuantity(1L, AbstractUnit.ONE)); + collectorStatistic = collector.process(clock, statistics); assertThat(collectorStatistic) .extracting(Quantity::getValue) .extracting(Number::doubleValue) diff --git a/bundles/org.connectorio.addons.network/pom.xml b/bundles/org.connectorio.addons.network/pom.xml index d3a5a7e73..3561981fb 100644 --- a/bundles/org.connectorio.addons.network/pom.xml +++ b/bundles/org.connectorio.addons.network/pom.xml @@ -37,6 +37,10 @@ org.openhab.core.bundles org.openhab.core + + org.connectorio.addons + org.connectorio.units + org.slf4j slf4j-api diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/Network.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/Network.java index a82041ee5..adebbfbde 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/Network.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/Network.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network; import org.openhab.core.common.registry.Identifiable; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkProvider.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkProvider.java index 737293a4f..e6cc8f02f 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkProvider.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkProvider.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network; import org.openhab.core.common.registry.Provider; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkRegistry.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkRegistry.java index 0be649237..c4ff576eb 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkRegistry.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkRegistry.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network; import java.util.stream.Stream; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkType.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkType.java index 484a1fb98..e523e7aae 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkType.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkType.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network; public interface NetworkType { diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkUID.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkUID.java index 76af5bc34..08c23356b 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkUID.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/NetworkUID.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network; import org.openhab.core.common.AbstractUID; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/CommonMetricCodes.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/CommonMetricCodes.java new file mode 100644 index 000000000..7c9f9bb64 --- /dev/null +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/CommonMetricCodes.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface; + +/** + * Most common statistic kinds for various network interfaces. + */ +public interface CommonMetricCodes { + + MetricCode RX_BYTES = new NamedMetricCode("rx_bytes"); + + MetricCode TX_BYTES = new NamedMetricCode("tx_bytes"); + + MetricCode RX_PACKETS = new NamedMetricCode("rx_packets"); + + MetricCode TX_PACKETS = new NamedMetricCode("tx_packets"); + +} diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceType.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricCode.java similarity index 79% rename from bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceType.java rename to bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricCode.java index 665ccd8ec..28513df8d 100644 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceType.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,10 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.can; +package org.connectorio.addons.network.iface; -@Deprecated -public interface CANInterfaceType { +public interface MetricCode { - String getType(); + String getCode(); } diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricsSupplier.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricsSupplier.java new file mode 100644 index 000000000..de3b02954 --- /dev/null +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/MetricsSupplier.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.network.iface; + +import java.util.Map; +import javax.measure.Quantity; + +/** + * Dedicated interface to collect network interface level metrics. + */ +public interface MetricsSupplier { + + Map> getMetrics(NetworkInterfaceUID nifid); + +} diff --git a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceTypes.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NamedMetricCode.java similarity index 58% rename from bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceTypes.java rename to bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NamedMetricCode.java index c76918729..ae0c5a223 100644 --- a/bundles/org.connectorio.addons.binding.can/src/main/java/org/connectorio/addons/binding/can/CANInterfaceTypes.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NamedMetricCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 ConnectorIO Sp. z o.o. + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,29 +15,21 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.connectorio.addons.binding.can; +package org.connectorio.addons.network.iface; import java.util.Objects; -@Deprecated -public interface CANInterfaceTypes { +public class NamedMetricCode implements MetricCode { - CANInterfaceType SOCKET_CAN = new SimpleInterface("socketcan"); + private final String code; -} - -@Deprecated -class SimpleInterface implements CANInterfaceType { - - private final String type; - - SimpleInterface(String type) { - this.type = type; + public NamedMetricCode(String code) { + this.code = code; } @Override - public String getType() { - return type; + public String getCode() { + return code; } @Override @@ -45,16 +37,20 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof SimpleInterface)) { + if (!(o instanceof MetricCode)) { return false; } - SimpleInterface that = (SimpleInterface) o; - return Objects.equals(getType(), that.getType()); + MetricCode that = (MetricCode) o; + return Objects.equals(code, that.getCode()); } @Override public int hashCode() { - return Objects.hash(getType()); + return Objects.hash(code); } + @Override + public String toString() { + return "[network interface metric=" + code + "]"; + } } \ No newline at end of file diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterface.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterface.java index b17d34227..05fbaace2 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterface.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterface.java @@ -1,6 +1,25 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; import java.util.List; +import java.util.Map; +import javax.measure.Quantity; import org.connectorio.addons.network.Network; import org.openhab.core.common.registry.Identifiable; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceProvider.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceProvider.java index c844e310f..7119792a0 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceProvider.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceProvider.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; import org.openhab.core.common.registry.Provider; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceRegistry.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceRegistry.java index 3e862d23e..f7f346313 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceRegistry.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceRegistry.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; import java.util.Collection; diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceStateCallback.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceStateCallback.java index f8aba2dc6..db3a46d29 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceStateCallback.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceStateCallback.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; public interface NetworkInterfaceStateCallback { diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceType.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceType.java index ee9341f1d..757ddd249 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceType.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceType.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; public interface NetworkInterfaceType { diff --git a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceUID.java b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceUID.java index 6f6fcad7b..3e84da01d 100644 --- a/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceUID.java +++ b/bundles/org.connectorio.addons.network/src/main/java/org/connectorio/addons/network/iface/NetworkInterfaceUID.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package org.connectorio.addons.network.iface; import java.util.Arrays; diff --git a/bundles/org.connectorio.bittpl/README.adoc b/bundles/org.connectorio.bittpl/README.adoc new file mode 100644 index 000000000..19ba89b14 --- /dev/null +++ b/bundles/org.connectorio.bittpl/README.adoc @@ -0,0 +1,81 @@ += Bit Template + +The Bit Template is a basic binary format specifier which rely on combination of hex and binary notation. +Its main purpose is definition of patterns which match byte payload. + +== Template format +Templates can be constructed from four elements described in table below. + +[cols="1,2,1,1"] +|=== +|Name | Pattern| Size (bit count)| Sample expr. + +| Nibble +| `'0' .. '9' \| 'a' .. 'f' \| 'A' .. 'F'` +| 4 +| `xAA` + +| Nibble variable +| `*` +| 4 +| `xA*` + +| Bit +| `'0' .. '9' \| 'a' .. 'f' \| 'A' .. 'F'` +| 1 +| `b0010` + +| Bit variable +| `.` +| 1 +| `b1.0.` + +|=== + +The template can be also used to create messages. + +When template is turned into message, variable part is defaulted to `0x00`. +Caller have to fill placeholders with proper values. +What is notable - writing does not have to follow variable parts. + +=== Example templates + +Below table list example templates and messages with their match status. + +[cols="1,1,2"] +|=== +|Template | Message | Matching / Notes + +| xAA xAB xAC +| AA AB AC 00 +| Yes / remaining part of message is not interpreted + +| xAA xAB xAC ** +| AA AB AC 00 +| Yes + +| xAA xAB xAC ** +| AA AB AB 00 +| No / third byte does not match `xAC` + +| xAA xAB xAC ** +| AA AB AC 01 +| Yes + +| xAA xAB xAC ** +| AA AB AC FF +| Yes + +| xAA xAB xAC b0000000. +| AA AB AC 00 +| Yes + +| xAA xAB xAC b0000000. +| AA AB AC 01 +| Yes + +| xAA xAB xAC b0000000. +| AA AB AC 02 +| No / bit representation of last byte is `00000010` + +|=== \ No newline at end of file diff --git a/bundles/org.connectorio.bittpl/pom.xml b/bundles/org.connectorio.bittpl/pom.xml new file mode 100644 index 000000000..5fd1c0889 --- /dev/null +++ b/bundles/org.connectorio.bittpl/pom.xml @@ -0,0 +1,95 @@ + + + + + 4.0.0 + + + org.connectorio.addons + bundles + 3.0.0-SNAPSHOT + + + org.connectorio.bittpl + bundle + + ConnectorIO - BitTPL + Bit Template. + + + + org.antlr + antlr4-runtime + + + com.github.jinahya + bit-io + ${bit-io.version} + + + + org.junit.jupiter + junit-jupiter-api + + + org.assertj + assertj-core + + + + + + + org.antlr + antlr4-maven-plugin + + + antlr + + antlr4 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-feature-xml + none + + + add-antlr-sources + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/antlr4/ + + + + + + + + + diff --git a/bundles/org.connectorio.bittpl/src/main/antlr4/org/connectorio/bittpl/parser/BitTPL.g4 b/bundles/org.connectorio.bittpl/src/main/antlr4/org/connectorio/bittpl/parser/BitTPL.g4 new file mode 100644 index 000000000..72e926cc2 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/antlr4/org/connectorio/bittpl/parser/BitTPL.g4 @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +grammar BitTPL; + +// This grammar is a basic definition of permitted inputs for byte based messages. +// It allows to define a selective templates and matching patterns. + +segments + : segment* EOF + ; + +segment + : ( binary | octet ) WS? + ; + +binary + : 'b' bit=bitSeq + ; + +octet + : 'x' hex=hexSeq + ; + + +hexSeq : HEX_SEQ+; +bitSeq : BIT_SEQ+; + +BIT_SEQ : BIT | BIT_ANY; +BIT_ANY : '.'; +BIT + : '0' + | '1' + ; + +HEX_SEQ : HEX_DIGIT | HEX_ANY; +HEX_ANY : '*'; +HEX_DIGIT + : '0' .. '9' + | 'a' .. 'f' + | 'A' .. 'F' + ; + + +WS + : ( ' ' ) -> channel ( HIDDEN ) + ; \ No newline at end of file diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/Template.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/Template.java new file mode 100644 index 000000000..836fd2c46 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/Template.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl; + +import com.github.jinahya.bit.io.ArrayByteInput; +import com.github.jinahya.bit.io.ArrayByteOutput; +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.DefaultBitInput; +import com.github.jinahya.bit.io.DefaultBitOutput; +import java.io.IOException; +import java.util.List; +import org.connectorio.bittpl.segment.Element; + +public class Template { + + protected final List elements; + private final int bitLength; + + public Template(List elements) { + this.elements = elements; + int count = 0; + for (Element element : elements) { + count += element.length(); + } + this.bitLength = count; + } + + public boolean matches(byte[] seq) { + ArrayByteInput byteInput = new ArrayByteInput(seq); + BitInput input = new DefaultBitInput(byteInput); + + // iterate over elements + for (int index = 0; index < elements.size(); index++) { + Element element = elements.get(index); + try { + boolean matches = element.matches(input); + if (!matches) { + return false; + } + } catch (IOException e) { + return false; + } + } + + return true; + } + + public byte[] toMessage() throws IOException { + byte[] target = new byte[bitLength / 8]; + DefaultBitOutput output = new DefaultBitOutput(new ArrayByteOutput(target)); + for (Element element : elements) { + element.write(output); + } + return target; + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateException.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateException.java new file mode 100644 index 000000000..a99bec458 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl; + +/** + * Error thrown in case when template parsing or processing fails. + */ +public class TemplateException extends RuntimeException { + + public TemplateException(String message) { + super(message); + } + + public TemplateException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateParser.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateParser.java new file mode 100644 index 000000000..4ca622b56 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/TemplateParser.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl; + +import java.util.ArrayList; +import java.util.List; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.connectorio.bittpl.parser.BitTPLBaseListener; +import org.connectorio.bittpl.parser.BitTPLLexer; +import org.connectorio.bittpl.parser.BitTPLParser; +import org.connectorio.bittpl.parser.BitTPLParser.BitSeqContext; +import org.connectorio.bittpl.parser.BitTPLParser.HexSeqContext; +import org.connectorio.bittpl.parser.TemplateErrorListener; +import org.connectorio.bittpl.segment.Bit; +import org.connectorio.bittpl.segment.Element; +import org.connectorio.bittpl.segment.Nibble; +import org.connectorio.bittpl.segment.BitWildcard; +import org.connectorio.bittpl.segment.NibbleWildcard; + +public class TemplateParser { + + public static Template parse(String template) { + BitTPLLexer lexer = new BitTPLLexer(CharStreams.fromString(template)); + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); + BitTPLParser parser = new BitTPLParser(new CommonTokenStream(lexer)); + parser.removeErrorListener(ConsoleErrorListener.INSTANCE); + parser.addErrorListener(new TemplateErrorListener()); + + Listener listener = new Listener(); + new ParseTreeWalker().walk(listener, parser.segments()); + + return new Template(listener.elements); + } + + static class Listener extends BitTPLBaseListener { + private final List elements = new ArrayList<>(); + + private int offset = 0; + private final static String STAR = "*"; + private final static String DOT = "."; + + @Override + public void enterHexSeq(HexSeqContext ctx) { + for (TerminalNode node : ctx.HEX_SEQ()) { + String nibble = node.getText(); + if (STAR.equals(nibble)) { + elements.add(new NibbleWildcard(offset)); + } else { + elements.add(new Nibble(offset, nibble)); + } + offset += 8; + } + } + + + @Override + public void enterBitSeq(BitSeqContext ctx) { + for (TerminalNode node : ctx.BIT_SEQ()) { + String bit = node.getText(); + if (DOT.equals(bit)) { + elements.add(new BitWildcard(offset)); + } else { + elements.add(new Bit(offset, bit.equals("1"))); + } + offset += 1; + } + } + + @Override + public void visitErrorNode(ErrorNode node) { + super.visitErrorNode(node); + throw new IllegalArgumentException(node.toString()); + } + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteInput.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteInput.java new file mode 100644 index 000000000..395ff1a82 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteInput.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.bitio; + +import com.github.jinahya.bit.io.ArrayByteInput; + +/** + * Byte input which supports arbitrary navigation over underlying buffer. + */ +public class BasicByteInput extends ArrayByteInput { + + public BasicByteInput(byte[] source) { + super(source); + } + + @Override + public void setIndex(int index) { + super.setIndex(index); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteOutput.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteOutput.java new file mode 100644 index 000000000..6218d6d43 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/bitio/BasicByteOutput.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.bitio; + +import com.github.jinahya.bit.io.ArrayByteOutput; + +/** + * Output which allows navigation over underlying buffer. + */ +public class BasicByteOutput extends ArrayByteOutput { + + public BasicByteOutput(byte[] target) { + super(target); + } + + public void setIndex(int index) { + super.setIndex(index); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/parser/TemplateErrorListener.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/parser/TemplateErrorListener.java new file mode 100644 index 000000000..2b3842087 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/parser/TemplateErrorListener.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.parser; + +import java.util.BitSet; +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.connectorio.bittpl.TemplateException; + +public class TemplateErrorListener implements ANTLRErrorListener { + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, + int charPositionInLine, String msg, RecognitionException e) { + + throw new TemplateException(msg + "(at " + line + ":" + charPositionInLine + ")"); + } + + @Override + public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, + boolean exact, BitSet ambigAlts, ATNConfigSet configs) { + + } + + @Override + public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, + BitSet conflictingAlts, ATNConfigSet configs) { + + } + + @Override + public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, + int prediction, ATNConfigSet configs) { + + } +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Bit.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Bit.java new file mode 100644 index 000000000..ab1fd1b82 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Bit.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.BitOutput; +import java.io.IOException; + +public class Bit extends ElementBase { + + private final boolean value; + + public Bit(int offset, boolean value) { + super(offset, 1); + this.value = value; + } + + @Override + public boolean matches(BitInput input) throws IOException { + boolean bit = input.readBoolean(); + return value == bit; + } + + @Override + public void write(BitOutput output) throws IOException { + output.writeBoolean(value); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/BitWildcard.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/BitWildcard.java new file mode 100644 index 000000000..d4513abcf --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/BitWildcard.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.BitOutput; +import java.io.IOException; + +public class BitWildcard extends ElementBase { + + public BitWildcard(int offset) { + super(offset, 1); + } + + @Override + public boolean matches(BitInput input) throws IOException { + input.readBoolean(); + return true; + } + + @Override + public void write(BitOutput output) throws IOException { + output.writeBoolean(false); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Element.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Element.java new file mode 100644 index 000000000..f0340b567 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Element.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.BitOutput; +import java.io.IOException; + +public interface Element { + + int offset(); + int length(); + + boolean matches(BitInput input) throws IOException; + + void write(BitOutput output) throws IOException; + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/ElementBase.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/ElementBase.java new file mode 100644 index 000000000..ce2188b89 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/ElementBase.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +public abstract class ElementBase implements Element { + + private final int offset; + private final int length; + + public ElementBase(int offset, int length) { + this.offset = offset; + this.length = length; + } + + @Override + public int offset() { + return offset; + } + + @Override + public int length() { + return length; + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Nibble.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Nibble.java new file mode 100644 index 000000000..a7d100a68 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/Nibble.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.BitOutput; +import java.io.IOException; + +public class Nibble extends ElementBase { + + private final byte value; + + public Nibble(int offset, String nibble) { + super(offset, 4); + this.value = Byte.parseByte(nibble, 16); + } + + @Override + public boolean matches(BitInput input) throws IOException { + int nibble = input.readByte(true, 4); + return value == nibble; + } + + @Override + public void write(BitOutput output) throws IOException { + output.writeByte(true, 4, value); + } + + @Override + public String toString() { + return "Nibble [" + offset() + "=" + this.value + "]"; + } + +} diff --git a/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/NibbleWildcard.java b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/NibbleWildcard.java new file mode 100644 index 000000000..a7beead06 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/main/java/org/connectorio/bittpl/segment/NibbleWildcard.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl.segment; + +import com.github.jinahya.bit.io.BitInput; +import com.github.jinahya.bit.io.BitOutput; +import java.io.IOException; + +public class NibbleWildcard extends ElementBase { + + public NibbleWildcard(int offset) { + super(offset, 4); + } + + @Override + public boolean matches(BitInput input) throws IOException { + input.readByte8(); + return true; + } + + @Override + public void write(BitOutput output) throws IOException { + output.writeByte(true, 4, (byte) 0x00); + } + +} diff --git a/bundles/org.connectorio.bittpl/src/test/java/org/connectorio/bittpl/TemplateParserTest.java b/bundles/org.connectorio.bittpl/src/test/java/org/connectorio/bittpl/TemplateParserTest.java new file mode 100644 index 000000000..d3fb45288 --- /dev/null +++ b/bundles/org.connectorio.bittpl/src/test/java/org/connectorio/bittpl/TemplateParserTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.bittpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +class TemplateParserTest { + + @Test + void testParser() throws Exception { + Template template = TemplateParser.parse("xAA xAB xAC b.... ...1"); + + byte[] message = new byte[] {(byte) 0xAA, (byte) 0xAB, (byte) 0xAC, 0x01}; + boolean matches = template.matches(message); + assertThat(matches).isTrue(); + + byte[] output = template.toMessage(); + assertThat(output).isEqualTo(message); + + message = new byte[] {(byte) 0xAA, (byte) 0xAB, (byte) 0xAC, 0x00}; + matches = template.matches(message); + assertThat(matches).isFalse(); + + message = new byte[] {(byte) 0xAA, (byte) 0xAB, (byte) 0xAC, 0x01, 0x00}; + matches = template.matches(message); + assertThat(matches).isTrue(); + } + + @Test + void testEmptyInput() throws IOException { + Template template = TemplateParser.parse(""); + assertThat(template.matches(new byte[0])) + .isTrue(); + + assertThat(template.toMessage()) + .isEqualTo(new byte[0]); + } + + @Test + void testInvalidHexInput() { + assertThatThrownBy(() -> TemplateParser.parse("a")) + .isInstanceOf(TemplateException.class) + .hasMessageContaining("at 1:0"); + } + +} \ No newline at end of file diff --git a/bundles/pom.xml b/bundles/pom.xml index 92d4c3efc..99d9ebd16 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -39,7 +39,7 @@ org.connectorio.addons.binding.amsads org.connectorio.addons.binding.askoheat org.connectorio.addons.binding.bacnet - org.connectorio.addons.binding.can + org.connectorio.addons.binding.canbus org.connectorio.addons.binding.canopen org.connectorio.addons.binding.canopen.ta org.connectorio.addons.binding.fatek @@ -68,9 +68,12 @@ org.connectorio.addons.managed.xstream org.connectorio.addons.mqtt org.connectorio.addons.network + org.connectorio.addons.network.can + org.connectorio.addons.network.can.dbus org.connectorio.addons.network.core org.connectorio.addons.network.ip org.connectorio.addons.network.jvm + org.connectorio.addons.network.linux org.connectorio.addons.network.transmitter org.connectorio.addons.network.transmitter.ip org.connectorio.addons.norule @@ -98,6 +101,7 @@ org.connectorio.addons.test org.connectorio.addons.transformation.inverse org.connectorio.addons.ui.iconify + org.connectorio.bittpl org.connectorio.chrono org.connectorio.logtail org.connectorio.logtail.web diff --git a/features/org.connectorio.addons.feature.canbus/pom.xml b/features/org.connectorio.addons.feature.canbus/pom.xml new file mode 100644 index 000000000..44cc66202 --- /dev/null +++ b/features/org.connectorio.addons.feature.canbus/pom.xml @@ -0,0 +1,122 @@ + + + + + 4.0.0 + + + org.connectorio.addons + features + 3.0.0-SNAPSHOT + + + org.connectorio.addons.feature.canbus + pom + + ConnectorIO - Addons - Features - CAN bus + CAN bus deployment archive. + + + + org.connectorio.addons + org.connectorio.addons.binding.canbus + + + + org.connectorio.addons + org.connectorio.addons.feature + features + xml + + + org.connectorio.addons + org.connectorio.addons.feature.network + features + xml + + + org.connectorio.addons + org.connectorio.addons.feature.plc4x + features + xml + + + org.connectorio.addons + org.connectorio.feature.bittpl + features + xml + + + + + org.apache.karaf.features + framework + kar + provided + + + + + + + ${basedir}/src/main/feature + true + ${project.build.directory}/feature + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + resources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.apache.karaf.tooling + karaf-maven-plugin + + + karaf-verification + + + mvn:org.connectorio.plc4x.extras.features/org.connectorio.plc4x.extras.feature.osgi/${plc4x-extras.version}/xml/features + mvn:org.connectorio.plc4x.extras.features/org.connectorio.plc4x.extras.feature.decorator/${plc4x-extras.version}/xml/features + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${openhab.version}/xml/features + mvn:org.connectorio.addons/org.connectorio.feature.bittpl/${project.version}/xml/features + + + co7io-binding-* + + + + + + + + + diff --git a/features/org.connectorio.addons.feature.canbus/src/main/feature/feature.xml b/features/org.connectorio.addons.feature.canbus/src/main/feature/feature.xml new file mode 100644 index 000000000..3d62b1523 --- /dev/null +++ b/features/org.connectorio.addons.feature.canbus/src/main/feature/feature.xml @@ -0,0 +1,43 @@ + + + + + mvn:org.connectorio.addons/org.connectorio.addons.feature.plc4x/${project.version}/xml/features + mvn:org.connectorio.addons/org.connectorio.addons.feature.network/${project.version}/xml/features + + + co7io-binding-canbus + + + + openhab-runtime-base + co7io-network-can + + co7io-bittpl + plc4j-transport-socketcan + plc4j-can-driver + co7io-binding-plc4x + co7io-plc4j-decorator-phase + co7io-plc4j-decorator-retry + co7io-plc4j-decorator-throttle + + mvn:org.connectorio.addons/org.connectorio.addons.binding.canbus/${project.version} + + + diff --git a/features/org.connectorio.addons.feature.canopen/pom.xml b/features/org.connectorio.addons.feature.canopen/pom.xml index 81e57bc0d..2ee73e537 100644 --- a/features/org.connectorio.addons.feature.canopen/pom.xml +++ b/features/org.connectorio.addons.feature.canopen/pom.xml @@ -33,10 +33,6 @@ CANopen deployment archive. - - org.connectorio.addons - org.connectorio.addons.binding.can - org.connectorio.addons org.connectorio.addons.binding.canopen @@ -52,6 +48,12 @@ features xml + + org.connectorio.addons + org.connectorio.addons.feature.network + features + xml + org.connectorio.addons org.connectorio.addons.feature.plc4x diff --git a/features/org.connectorio.addons.feature.canopen/src/main/feature/feature.xml b/features/org.connectorio.addons.feature.canopen/src/main/feature/feature.xml index 9c0b1a3da..e825f2252 100644 --- a/features/org.connectorio.addons.feature.canopen/src/main/feature/feature.xml +++ b/features/org.connectorio.addons.feature.canopen/src/main/feature/feature.xml @@ -19,47 +19,23 @@ mvn:org.connectorio.addons/org.connectorio.addons.feature.plc4x/${project.version}/xml/features - - - co7io-binding-can - - - - wrap - mvn:org.ow2.asm/asm/7.3.1 - - mvn:com.github.jnr/jnr-unixsocket/0.33 - mvn:com.github.jnr/jnr-constants/0.9.15 - mvn:com.github.jnr/jnr-enxio/0.28 - mvn:com.github.jnr/jnr-ffi/2.1.15 - mvn:com.github.jnr/jnr-posix/3.0.58 - wrap:mvn:com.github.jnr/jnr-a64asm/1.0.0 - wrap:mvn:com.github.jnr/jnr-x86asm/1.0.2 - mvn:com.github.jnr/jffi/1.2.23/jar/complete - wrap:mvn:com.github.jnr/jffi/1.2.23/jar/native - wrap:mvn:com.github.hypfvieh/dbus-java/3.2.3 - wrap:mvn:com.github.hypfvieh/java-utils/1.0.6 - - - - openhab-runtime-base - dbus - mvn:org.connectorio.addons/org.connectorio.addons.binding.can/${project.version} - - + mvn:org.connectorio.addons/org.connectorio.addons.feature.network/${project.version}/xml/features - co7io-binding-plc4x-canopen + co7io-binding-canopen openhab-runtime-base + co7io-network-can + + plc4j-transport-socketcan plc4j-canopen-driver co7io-binding-plc4x - co7io-binding-can co7io-plc4j-decorator-phase co7io-plc4j-decorator-retry co7io-plc4j-decorator-throttle + mvn:org.connectorio.addons/org.connectorio.addons.binding.canopen/${project.version} @@ -72,4 +48,5 @@ mvn:org.connectorio.addons/org.connectorio.addons.binding.canopen.ta/${project.version} + diff --git a/features/org.connectorio.addons.feature.network/pom.xml b/features/org.connectorio.addons.feature.network/pom.xml index ffbd868b4..103bb4af7 100644 --- a/features/org.connectorio.addons.feature.network/pom.xml +++ b/features/org.connectorio.addons.feature.network/pom.xml @@ -37,6 +37,14 @@ org.connectorio.addons org.connectorio.addons.network + + org.connectorio.addons + org.connectorio.addons.network.can + + + org.connectorio.addons + org.connectorio.addons.network.can.dbus + org.connectorio.addons org.connectorio.addons.network.core @@ -49,6 +57,10 @@ org.connectorio.addons org.connectorio.addons.network.jvm + + org.connectorio.addons + org.connectorio.addons.network.linux + org.connectorio.addons org.connectorio.addons.network.transmitter diff --git a/features/org.connectorio.addons.feature.network/src/main/feature/feature.xml b/features/org.connectorio.addons.feature.network/src/main/feature/feature.xml index 0dfcbcb29..05a52e7fa 100644 --- a/features/org.connectorio.addons.feature.network/src/main/feature/feature.xml +++ b/features/org.connectorio.addons.feature.network/src/main/feature/feature.xml @@ -27,6 +27,24 @@ mvn:org.connectorio.addons/org.connectorio.addons.network/${project.version} + + co7io-network-api + + mvn:org.connectorio.addons/org.connectorio.addons.network.can/${project.version} + + + req:os.name=Linux + co7io-network-can-dbus + + + + + co7io-network-can + dbus + + mvn:org.connectorio.addons/org.connectorio.addons.network.can.dbus/${project.version} + + co7io-network-api @@ -63,4 +81,21 @@ co7io-network-jvm + + wrap + mvn:org.ow2.asm/asm/7.3.1 + + mvn:com.github.jnr/jnr-unixsocket/0.33 + mvn:com.github.jnr/jnr-constants/0.9.15 + mvn:com.github.jnr/jnr-enxio/0.28 + mvn:com.github.jnr/jnr-ffi/2.1.15 + mvn:com.github.jnr/jnr-posix/3.0.58 + wrap:mvn:com.github.jnr/jnr-a64asm/1.0.0 + wrap:mvn:com.github.jnr/jnr-x86asm/1.0.2 + mvn:com.github.jnr/jffi/1.2.23/jar/complete + wrap:mvn:com.github.jnr/jffi/1.2.23/jar/native + wrap:mvn:com.github.hypfvieh/dbus-java/3.2.3 + wrap:mvn:com.github.hypfvieh/java-utils/1.0.6 + + diff --git a/features/org.connectorio.feature.bittpl/pom.xml b/features/org.connectorio.feature.bittpl/pom.xml new file mode 100644 index 000000000..e1423c3bc --- /dev/null +++ b/features/org.connectorio.feature.bittpl/pom.xml @@ -0,0 +1,99 @@ + + + + + 4.0.0 + + + org.connectorio.addons + features + 3.0.0-SNAPSHOT + + + org.connectorio.feature.bittpl + pom + + ConnectorIO - Features - BitTpl + Bit template feature. + + + + org.connectorio.addons + org.connectorio.bittpl + + + + + org.apache.karaf.features + framework + kar + provided + + + + + + + ${basedir}/src/main/feature + true + ${project.build.directory}/feature + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + resources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + org.apache.karaf.tooling + karaf-maven-plugin + + + karaf-verification + + + co7io-* + + + + + aggregate-descriptor + none + + + oh-kar + none + + + + + + + diff --git a/features/org.connectorio.feature.bittpl/src/main/feature/feature.xml b/features/org.connectorio.feature.bittpl/src/main/feature/feature.xml new file mode 100644 index 000000000..ba8c7bb93 --- /dev/null +++ b/features/org.connectorio.feature.bittpl/src/main/feature/feature.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/features/pom.xml b/features/pom.xml index 80ec0d591..0cff4de83 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -38,6 +38,7 @@ org.connectorio.addons.feature.amsads org.connectorio.addons.feature.askoheat org.connectorio.addons.feature.bacnet + org.connectorio.addons.feature.canbus org.connectorio.addons.feature.canopen org.connectorio.addons.feature.fatek org.connectorio.addons.feature.io @@ -57,6 +58,7 @@ org.connectorio.addons.feature.s7 org.connectorio.addons.feature.smartme org.connectorio.addons.feature.wmbus + org.connectorio.feature.bittpl org.connectorio.feature.chrono diff --git a/itests/org.connectorio.addons.itest.canbus/pom.xml b/itests/org.connectorio.addons.itest.canbus/pom.xml new file mode 100644 index 000000000..44b719c37 --- /dev/null +++ b/itests/org.connectorio.addons.itest.canbus/pom.xml @@ -0,0 +1,59 @@ + + + + + 4.0.0 + + + org.connectorio.addons + itests + 3.0.0-SNAPSHOT + + + org.connectorio.addons.itest.canbus + + ConnectorIO - Addons - ITests - CAN bus + Integration tests of CAN. + + + + org.connectorio.addons + org.connectorio.addons.kar.canbus + kar + + + + org.connectorio.addons + org.connectorio.addons.itest + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + org.connectorio.addons.kar.canbus + + + + + + diff --git a/itests/org.connectorio.addons.itest.canbus/src/test/java/org/connectorio/addons/itest/canbus/CANbusOfflineKarInstallationTest.java b/itests/org.connectorio.addons.itest.canbus/src/test/java/org/connectorio/addons/itest/canbus/CANbusOfflineKarInstallationTest.java new file mode 100644 index 000000000..0331b694c --- /dev/null +++ b/itests/org.connectorio.addons.itest.canbus/src/test/java/org/connectorio/addons/itest/canbus/CANbusOfflineKarInstallationTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023-2023 ConnectorIO Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.connectorio.addons.itest.canbus; + +import org.connectorio.addons.itest.OfflineKarInstallationTest; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class CANbusOfflineKarInstallationTest extends OfflineKarInstallationTest { + + public CANbusOfflineKarInstallationTest() { + super("canbus", "binding", "co7io-canbus"); + } + +} diff --git a/itests/pom.xml b/itests/pom.xml index a771ee52f..91fad7b6b 100644 --- a/itests/pom.xml +++ b/itests/pom.xml @@ -37,6 +37,7 @@ org.connectorio.addons.itest org.connectorio.addons.itest.amsads org.connectorio.addons.itest.bacnet + org.connectorio.addons.itest.canbus org.connectorio.addons.itest.canopen org.connectorio.addons.itest.fatek org.connectorio.addons.itest.io.transport.serial diff --git a/kars/org.connectorio.addons.kar.canbus/pom.xml b/kars/org.connectorio.addons.kar.canbus/pom.xml new file mode 100644 index 000000000..91feeb835 --- /dev/null +++ b/kars/org.connectorio.addons.kar.canbus/pom.xml @@ -0,0 +1,80 @@ + + + + + 4.0.0 + + + org.connectorio.addons + kars + 3.0.0-SNAPSHOT + + + org.connectorio.addons.kar.canbus + kar + + ConnectorIO - Addons - KARs - CAN bus + CAN bus deployment archive dedicated to openHAB runtime. + + + + org.connectorio.addons + org.connectorio.addons.feature.canbus + features + xml + + + org.connectorio.addons + org.connectorio.addons.feature + features + xml + + + org.connectorio.addons + org.connectorio.addons.feature.network + features + xml + + + org.connectorio.addons + org.connectorio.feature.bittpl + features + xml + + + org.connectorio.addons + org.connectorio.addons.feature.plc4x + features + xml + + + org.connectorio.plc4x.extras.features + org.connectorio.plc4x.extras.feature.osgi + xml + features + + + org.connectorio.plc4x.extras.features + org.connectorio.plc4x.extras.feature.decorator + xml + features + + + + diff --git a/kars/org.connectorio.addons.kar.canopen/pom.xml b/kars/org.connectorio.addons.kar.canopen/pom.xml index c7f64355d..12e8302e1 100644 --- a/kars/org.connectorio.addons.kar.canopen/pom.xml +++ b/kars/org.connectorio.addons.kar.canopen/pom.xml @@ -45,6 +45,12 @@ features xml + + org.connectorio.addons + org.connectorio.addons.feature.network + features + xml + org.connectorio.addons org.connectorio.addons.feature.plc4x diff --git a/kars/pom.xml b/kars/pom.xml index 48f3c5033..53b8aeb07 100644 --- a/kars/pom.xml +++ b/kars/pom.xml @@ -36,6 +36,7 @@ org.connectorio.addons.kar.amsads org.connectorio.addons.kar.bacnet + org.connectorio.addons.kar.canbus org.connectorio.addons.kar.canopen org.connectorio.addons.kar.fatek org.connectorio.addons.kar.io.transport.serial diff --git a/parent/pom.xml b/parent/pom.xml index 65316dae9..6d5a3698a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -37,8 +37,8 @@ 11 11 - 0.12.0 - 1.3.0-beta1 + 0.13.0-connectorio-2 + 1.3.0-beta3 3.0.4 3.0.0-alpha-4 @@ -69,6 +69,9 @@ 3.0.1 1.0.1 1.17.6 + 4.13.1 + + 2.0.5 @@ -108,6 +111,20 @@ features xml + + + org.connectorio.addons + org.connectorio.bittpl + ${project.version} + + + org.connectorio.addons + org.connectorio.feature.bittpl + ${project.version} + features + xml + + org.connectorio.addons org.connectorio.addons.communication.watchdog @@ -179,12 +196,6 @@ kar - - org.connectorio.addons - org.connectorio.addons.binding.can - ${project.version} - - org.connectorio.addons org.connectorio.addons.binding.fatek @@ -301,9 +312,23 @@ org.connectorio.addons - org.connectorio.addons.binding.plc4x.can + org.connectorio.addons.binding.canbus + ${project.version} + + + org.connectorio.addons + org.connectorio.addons.feature.canbus + ${project.version} + features + xml + + + org.connectorio.addons + org.connectorio.addons.kar.canbus ${project.version} + kar + org.connectorio.addons org.connectorio.addons.binding.canopen @@ -614,6 +639,16 @@ org.connectorio.addons.network ${project.version} + + org.connectorio.addons + org.connectorio.addons.network.can + ${project.version} + + + org.connectorio.addons + org.connectorio.addons.network.can.dbus + ${project.version} + org.connectorio.addons org.connectorio.addons.network.core @@ -629,6 +664,11 @@ org.connectorio.addons.network.jvm ${project.version} + + org.connectorio.addons + org.connectorio.addons.network.linux + ${project.version} + org.connectorio.addons org.connectorio.addons.network.transmitter @@ -923,12 +963,23 @@ jrxtx ${jrxtx.version} + + + org.antlr + antlr4-runtime + ${antlr4.version} + + + org.antlr + antlr4-maven-plugin + ${antlr4.version} + org.apache.felix maven-bundle-plugin