Skip to content

Commit

Permalink
Rename can binding to canbus, invent bittpl.
Browse files Browse the repository at this point in the history
BitTpl stands for bit template, a generic purpose binary payload matcher.

Signed-off-by: Łukasz Dywicki <[email protected]>
  • Loading branch information
splatch committed Nov 5, 2023
1 parent 382f033 commit d570c68
Show file tree
Hide file tree
Showing 50 changed files with 1,770 additions and 41 deletions.
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@
<version>3.0.0-SNAPSHOT</version>
</parent>

<artifactId>org.connectorio.addons.binding.can</artifactId>
<artifactId>org.connectorio.addons.binding.canbus</artifactId>
<packaging>bundle</packaging>

<name>ConnectorIO - Addons - Binding - CAN</name>
<name>ConnectorIO - Addons - Binding - CAN bus</name>
<description>CAN bus integration.</description>

<dependencies>
<dependency>
<groupId>org.connectorio.addons</groupId>
<artifactId>org.connectorio.bittpl</artifactId>
</dependency>
<dependency>
<groupId>org.connectorio.addons</groupId>
<artifactId>org.connectorio.addons.network.can</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ThingTypeUID> SUPPORTED_THINGS = Set.of(SOCKETCAN_BRIDGE_THING_TYPE, MESSAGE_THING_TYPE);
}
Original file line number Diff line number Diff line change
@@ -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<Void> write(int cob, byte[] data);

// cancellable subscription
Future<Void> subscribe(int cob, Consumer<byte[]> callback);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.connectorio.addons.binding.canbus.config;

public class MessageConfiguration {

int cob;
String template;

}
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.connectorio.addons.binding.canbus.config.channel;

public class ContactChannelConfig extends BaseOffsetConfig {

String openValue;
String closedValue;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.connectorio.addons.binding.canbus.config.channel;

public interface LengthConfig {

int length();

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.connectorio.addons.binding.canbus.config.channel;

public interface OffsetConfig {

int offset();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.connectorio.addons.binding.canbus.config.channel;

public class SwitchChannelConfig extends BaseOffsetConfig {

String onValue;
String offValue;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.connectorio.addons.binding.canbus.discovery;

// TBD
public interface CANDiscoveryParticipant {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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.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<Void> 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) {
List<PlcUSINT> values = new ArrayList<>();
for (byte b : data) {
values.add(new PlcUSINT(b));
}

return PlcValues.of(values);
}

@Override
public Future<Void> subscribe(int cob, Consumer<byte[]> callback) {
GenericCANTag tag = new GenericCANTag(cob, GenericCANDataType.UNSIGNED8, 8);

final AtomicReference<PlcConsumerRegistration> registration = new AtomicReference<>();
CompletableFuture<Void> 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<PlcSubscriptionEvent>() {
@Override
public void accept(PlcSubscriptionEvent plcSubscriptionEvent) {
Collection<Short> data = plcSubscriptionEvent.getAllShorts("data");
callback.accept(decode(new ArrayList<>(data)));
}
});
registration.set(reg);
return registration;
});

return ret;
}

private byte[] decode(List<Short> data) {
byte[] bytes = new byte[data.size()];
for (int index = 0; index < bytes.length; index++) {
bytes[index] = data.get(index).byteValue();
}
return bytes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* 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})
public class CANInterfaceDiscoveryDelegate extends AbstractDiscoveryService implements DiscoveryService,
NetworkInterfaceStateCallback {

private static final Set<ThingTypeUID> 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());
}

}
Loading

0 comments on commit d570c68

Please sign in to comment.