org.bidib.wizard.simulation.OpenSwitchSimulator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-simulation Show documentation
Show all versions of bidibwizard-simulation Show documentation
jBiDiB BiDiB Wizard Simulation POM
The newest version!
package org.bidib.wizard.simulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.BidibPort;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.LcConfigX;
import org.bidib.jbidibc.messages.enums.LcOutputType;
import org.bidib.jbidibc.messages.enums.SwitchPortEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.AccessoryGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaResponse;
import org.bidib.jbidibc.messages.message.AccessorySetMessage;
import org.bidib.jbidibc.messages.message.AccessoryStateResponse;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.LcConfigXGetMessage;
import org.bidib.jbidibc.messages.message.LcConfigXResponse;
import org.bidib.jbidibc.messages.message.LcConfigXSetMessage;
import org.bidib.jbidibc.messages.message.LcNotAvailableResponse;
import org.bidib.jbidibc.messages.message.LcOutputMessage;
import org.bidib.jbidibc.messages.message.LcPortQueryMessage;
import org.bidib.jbidibc.messages.message.LcStatResponse;
import org.bidib.jbidibc.messages.port.BytePortConfigValue;
import org.bidib.jbidibc.messages.port.PortConfigValue;
import org.bidib.jbidibc.messages.port.ReconfigPortConfigValue;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.simulation.SimulationBidibMessageProcessor;
import org.bidib.jbidibc.simulation.SwitchingFunctionsNode;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulator;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulators;
import org.bidib.wizard.model.ports.GenericPort;
import org.bidib.wizard.model.status.SwitchPortStatus;
import org.bidib.wizard.simulation.events.SwitchPairPortStatusEvent;
import org.bidib.wizard.simulation.events.SwitchPortStatusEvent;
import org.bidib.wizard.simulation.ports.GenericSimulationPort;
import org.bushe.swing.event.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@BidibNodeSimulators({ @BidibNodeSimulator(vid = "13", pid = "146"), @BidibNodeSimulator(vid = "251", pid = "227") })
public class OpenSwitchSimulator extends LightControlSimulator implements SwitchingFunctionsNode {
private static final Logger LOGGER = LoggerFactory.getLogger(OpenSwitchSimulator.class);
private final Map genericPorts = new HashMap();
protected final ScheduledExecutorService simulationWorker = Executors.newScheduledThreadPool(1);
public OpenSwitchSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {
super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);
}
@Override
public void postConstruct() {
// super.postConstruct();
Feature feature = getFeature(BidibLibrary.FEATURE_CTRL_PORT_FLAT_MODEL);
LOGGER.info("The current simulator has the flat model configured. Prepare the generic ports.");
int numPorts = feature.getValue();
LOGGER.info("Create generic ports, numPorts: {}", numPorts);
for (int portNumber = 0; portNumber < numPorts; portNumber++) {
GenericSimulationPort port = new GenericSimulationPort(portNumber);
port.setPortValue(250);
port.setPortStatus(SwitchPortStatus.OFF.getType().getType());
port
.setCurrentPortType(LcOutputType.SWITCHPAIRPORT,
1 << BidibLibrary.BIDIB_PORTTYPE_SWITCHPAIR | 1 << BidibLibrary.BIDIB_PORTTYPE_SWITCH);
genericPorts.put(portNumber, port);
}
}
@Override
protected void prepareFeatures() {
LOGGER.info("Prepare the features.");
// super.prepareFeatures();
}
@Override
public void start() {
LOGGER.info("Start the simulator for address: {}", getAddress());
super.start();
if (!MapUtils.isEmpty(genericPorts)) {
// OpenSwitch has 8 switch ports
this.switchPortCount = 8;
int index = 0;
for (int portNum = 0; portNum < switchPortCount; portNum++) {
GenericSimulationPort genericPort = genericPorts.get(portNum + index);
// only even port can be a switchpair port
int mask = (1 << BidibLibrary.BIDIB_PORTTYPE_SWITCH | 1 << BidibLibrary.BIDIB_PORTTYPE_SWITCHPAIR);
if ((portNum & 0x01) == 0x01) {
mask = (1 << BidibLibrary.BIDIB_PORTTYPE_SWITCH);
genericPort.setCurrentPortType(LcOutputType.SWITCHPORT, mask);
}
else {
// switchPair ports are active by default
genericPort.setCurrentPortType(LcOutputType.SWITCHPAIRPORT, mask);
}
Map> portConfig = genericPort.getPortConfigX();
portConfig.put(BidibLibrary.BIDIB_PCFG_LOAD_TYPE, new BytePortConfigValue(Byte.valueOf((byte) 0x00)));
portConfig.put(BidibLibrary.BIDIB_PCFG_TICKS, new BytePortConfigValue(Byte.valueOf((byte) 0x0F)));
genericPort.setPortConfigX(portConfig);
}
}
}
@Override
public void stop() {
super.stop();
}
@Override
protected byte[] processLcConfigXSetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the LcConfigXSet request: {}", bidibMessage);
byte[] response = null;
if (getFlatPortModelPortCount() > 0) {
try {
LcConfigXSetMessage lcConfigXSetMessage = (LcConfigXSetMessage) bidibMessage;
int outputNumber = lcConfigXSetMessage.getPortNumber(getPortModel());
LcOutputType outputType = lcConfigXSetMessage.getPortType(getPortModel());
GenericPort port = null;
port = genericPorts.get(Integer.valueOf(outputNumber));
LOGGER.info("Set LcConfig for output number: {}, port: {}", outputNumber, port);
BidibPort bidibPort = prepareBidibPort(getPortModel(), outputType, outputNumber);
if (port != null) {
Map> currentPortConfig = port.getPortConfigX();
// the problem here is that the BIDIB_PCFG_RECONFIG overwrites the supported port types
LcConfigX lcConfigX = lcConfigXSetMessage.getLcConfigX(messageLogger);
ReconfigPortConfigValue reconfig =
(ReconfigPortConfigValue) lcConfigX.getPortConfig().get(BidibLibrary.BIDIB_PCFG_RECONFIG);
if (reconfig != null) {
// we must keep the supported port types
Integer supportedPortTypes = port.getSupportedPortTypes();
if (supportedPortTypes != null) {
ReconfigPortConfigValue newReconfig =
new ReconfigPortConfigValue(reconfig.getCurrentOutputType().getType(),
supportedPortTypes);
LOGGER.info("Prepared BIDIB_PCFG_RECONFIG to replace: {}", newReconfig);
lcConfigX.getPortConfig().put(BidibLibrary.BIDIB_PCFG_RECONFIG, newReconfig);
}
}
// port.setPortConfig(lcConfigX.getPortConfig());
for (Entry> entry : lcConfigX.getPortConfig().entrySet()) {
Byte key = entry.getKey();
if (currentPortConfig.containsKey(key)) {
currentPortConfig.remove(key);
}
currentPortConfig.put(key, entry.getValue());
}
port.setPortConfigX(currentPortConfig);
lcConfigX.getPortConfig().clear();
lcConfigX.getPortConfig().putAll(currentPortConfig);
// // TODO remove this test
// if (outputNumber == 5) {
// LOGGER.warn("Change the content to signal an error ...");
// lcConfigX.getPortConfig().put(BidibLibrary.BIDIB_PCFG_NONE, new BytePortConfigValue((byte) 12));
// }
byte[] content = LcConfigX.getCodedPortConfig(null, lcConfigX, getPortModel());
LOGGER.info("Prepared content: {}", ByteUtils.bytesToHex(content));
LcConfigXResponse lcConfigXResponse =
new LcConfigXResponse(bidibMessage.getAddr(), getNextSendNum(), content);
response = lcConfigXResponse.getContent();
}
else {
LOGGER.warn("No port assigned!");
LcNotAvailableResponse magicResponse =
new LcNotAvailableResponse(bidibMessage.getAddr(), getNextSendNum(), bidibPort);
response = magicResponse.getContent();
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create LcConfigX response failed.", ex);
}
}
else {
response = super.processLcConfigXSetRequest(bidibMessage);
}
return response;
}
@Override
protected byte[] processLcConfigXGetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the LcConfigXGet request: {}", bidibMessage);
byte[] response = null;
if (getFlatPortModelPortCount() > 0) {
try {
LcConfigXGetMessage lcConfigXGetMessage = (LcConfigXGetMessage) bidibMessage;
int outputNumber = lcConfigXGetMessage.getPortNumber(getPortModel());
LcOutputType outputType = lcConfigXGetMessage.getPortType(getPortModel());
GenericPort port = null;
Map> values = new LinkedHashMap<>();
port = genericPorts.get(Integer.valueOf(outputNumber));
BidibPort bidibPort = prepareBidibPort(getPortModel(), outputType, outputNumber);
if (port != null) {
values.putAll(port.getPortConfigX());
// // TODO remove this test
// if (outputNumber == 2) {
// values.put(BidibLibrary.BIDIB_PCFG_NONE, new BytePortConfigValue((byte) 12));
// }
LOGGER.info("Return config of port: {}", port);
LcConfigX lcConfigX = new LcConfigX(bidibPort, values);
LcConfigXResponse lcConfigXResponse =
new LcConfigXResponse(bidibMessage.getAddr(), getNextSendNum(),
LcConfigX.getCodedPortConfig(null, lcConfigX, getPortModel()));
response = lcConfigXResponse.getContent();
}
else {
LOGGER.warn("No port available with port number: {}", outputNumber);
LcNotAvailableResponse notAvailableResponse =
new LcNotAvailableResponse(bidibMessage.getAddr(), getNextSendNum(), bidibPort);
response = notAvailableResponse.getContent();
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create LcConfigX response failed.", ex);
}
}
else {
response = super.processLcConfigXGetRequest(bidibMessage);
}
return response;
}
@Override
protected void processLcConfigXGetAllRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the LcConfigXGetAll request: {}", bidibMessage);
if (isPortFlatModelAvailable()) {
LcOutputType outputType = LcOutputType.SWITCHPORT;
Map> values = new LinkedHashMap<>();
try {
for (GenericPort port : genericPorts.values()) {
int portNumber = port.getPortNumber();
LOGGER.info("Prepare lcConfigXResponse for port number: {}", portNumber);
values.clear();
values.putAll(port.getPortConfigX());
// // TODO remove this test
// if (portNumber == 6) {
// values.put(BidibLibrary.BIDIB_PCFG_NONE, new BytePortConfigValue((byte) 12));
// }
LOGGER.info("Return config of port: {}", port);
BidibPort bidibPort = prepareBidibPort(getPortModel(), outputType, portNumber);
LcConfigX lcConfigX = new LcConfigX(bidibPort, values);
LcConfigXResponse lcConfigXResponse =
new LcConfigXResponse(bidibMessage.getAddr(), getNextSendNum(),
LcConfigX.getCodedPortConfig(null, lcConfigX, getPortModel()));
byte[] response = lcConfigXResponse.getContent();
LOGGER.info("Prepared lcConfigXResponse: {}", ByteUtils.bytesToHex(response));
sendSpontanousResponse(response);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create LcConfigXResponse response failed.", ex);
}
}
else {
super.processLcConfigXGetAllRequest(bidibMessage);
}
}
@Override
protected byte[] processLcOutputRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the LcOutput request: {}", bidibMessage);
byte[] response = null;
if (getFlatPortModelPortCount() > 0) {
try {
LcOutputMessage lcOutputMessage = (LcOutputMessage) bidibMessage;
LcOutputType outputType = lcOutputMessage.getOutputType(getPortModel());
int outputNumber = lcOutputMessage.getOutputNumber(getPortModel());
byte outputStatus = lcOutputMessage.getOutputStatus();
GenericPort port = genericPorts.get(Integer.valueOf(outputNumber));
BidibPort bidibPort = prepareBidibPort(getPortModel(), outputType, outputNumber);
if (port != null) {
LcStatResponse lcStatResponse = null;
port.setPortStatus(outputStatus);
lcStatResponse =
new LcStatResponse(bidibMessage.getAddr(), getNextSendNum(), bidibPort,
lcOutputMessage.getOutputStatus());
response = lcStatResponse.getContent();
}
else {
LOGGER.warn("No port available with portNumber: {}", outputNumber);
LcNotAvailableResponse lcNotAvailableResponse =
new LcNotAvailableResponse(bidibMessage.getAddr(), getNextSendNum(), bidibPort);
response = lcNotAvailableResponse.getContent();
}
if (port != null) {
switch (outputType) {
case SWITCHPORT:
publishSwitchPortChange(port);
break;
case SWITCHPAIRPORT:
publishSwitchPairPortChange(port);
break;
default:
break;
}
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create LcStat response failed.", ex);
}
}
else {
response = super.processLcOutputRequest(bidibMessage);
}
return response;
}
@Override
protected byte[] processLcPortQueryRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the LcOutputQuery request: {}", bidibMessage);
byte[] response = null;
if (isPortFlatModelAvailable()) {
byte portState = 0;
try {
LcPortQueryMessage lcOutputQueryMessage = (LcPortQueryMessage) bidibMessage;
LcOutputType outputType = lcOutputQueryMessage.getPortType(getPortModel());
int outputNumber = lcOutputQueryMessage.getPortNumber(getPortModel());
GenericPort port = genericPorts.get(Integer.valueOf(outputNumber));
if (port != null) {
portState = port.getPortStatus();
}
else {
LOGGER.warn("No port available with portNumber: {}", outputNumber);
}
BidibPort bidibPort = prepareBidibPort(getPortModel(), outputType, outputNumber);
LcStatResponse lcStatResponse =
new LcStatResponse(bidibMessage.getAddr(), getNextSendNum(), bidibPort, portState);
response = lcStatResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create LcStat response failed.", ex);
}
}
else {
response = super.processLcPortQueryRequest(bidibMessage);
}
return response;
}
private void publishSwitchPortChange(GenericPort port) {
SwitchPortStatus status = SwitchPortStatus.valueOf(SwitchPortEnum.valueOf(port.getPortStatus()));
LOGGER.info("The switchport status has changed, notify the listeners, nodeAddress: {}", nodeAddress);
EventBus.publish(new SwitchPortStatusEvent(NodeUtils.formatAddress(nodeAddress), port.getPortNumber(), status));
}
private void publishSwitchPairPortChange(GenericPort port) {
SwitchPortStatus status = SwitchPortStatus.valueOf(SwitchPortEnum.valueOf(port.getPortStatus()));
LOGGER.info("The switchPairPort status has changed, notify the listeners, nodeAddress: {}", nodeAddress);
EventBus
.publish(new SwitchPairPortStatusEvent(NodeUtils.formatAddress(nodeAddress), port.getPortNumber(), status));
}
//////////////////////////////////////////////////////////
private boolean simulateAccessoryStateError;
@Override
protected byte[] processAccessorySetRequest(final BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessorySet request: {}", bidibMessage);
byte[] response = null;
try {
AccessorySetMessage accessorySetMessage = (AccessorySetMessage) bidibMessage;
int accessoryNumber = accessorySetMessage.getAccessoryNumber();
int aspect = accessorySetMessage.getAspect();
byte[] value = new byte[] { 0, 0, 0 };
if (simulateAccessoryStateError && accessoryNumber == 1 && aspect == 1) {
LOGGER.warn("Adding simulated error to accessory state response");
value = new byte[] { 2, 1, 20 };
final AccessoryResponseHandler callable =
new AccessoryResponseHandler(bidibMessage.getAddr(), accessoryNumber, aspect);
ScheduledFuture> future =
simulationWorker.scheduleWithFixedDelay(callable, 1500, 1500, TimeUnit.MILLISECONDS);
callable.setFuture(future);
}
// send accessory state response
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(),
ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(aspect), value);
response = accessoryStateResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
return response;
}
private final class AccessoryResponseHandler implements Runnable {
private List accessoryResponseList = new ArrayList<>();
private final byte[] nodeAddress;
private ScheduledFuture> future;
public AccessoryResponseHandler(byte[] nodeAddress, int accessoryNumber, int aspect) {
this.nodeAddress = nodeAddress;
// add the remaining response values
accessoryResponseList.add(accessoryNumber + "," + aspect + ",2,128,7");
accessoryResponseList.add(accessoryNumber + ",255,2,129,7");
accessoryResponseList.add(accessoryNumber + ",255,2,0,0");
}
public void setFuture(ScheduledFuture> future) {
LOGGER.info("Set the future: {}", future);
this.future = future;
}
@Override
public void run() {
LOGGER.info("The AccessoryResponseHandler is executed.");
if (!accessoryResponseList.isEmpty()) {
String accessoryResponse = accessoryResponseList.remove(0);
if (StringUtils.isNotBlank(accessoryResponse)) {
LOGGER.info("Send response: {}", accessoryResponse);
// parse
String[] parts = accessoryResponse.split(",");
byte[] value = new byte[parts.length];
int index = 0;
for (String part : parts) {
int val = Integer.parseInt(part);
value[index] = ByteUtils.getLowByte(val);
index++;
}
try {
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(nodeAddress, 0, /* accessoryNumber */value[0],
/* aspect */value[1], ByteUtils.subArray(value, 2));
byte[] response = accessoryStateResponse.getContent();
LOGGER.info("Prepared accessoryStateResponse: {}", accessoryStateResponse);
OpenSwitchSimulator.this.sendSpontanousResponse(response);
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
}
}
if (accessoryResponseList.isEmpty()) {
LOGGER.info("No more entries in accessoryResponseList. Cancel the future.");
if (future != null) {
try {
future.cancel(false);
}
catch (Exception ex) {
LOGGER.warn("Cancel the future failed.", ex);
}
}
}
}
}
@Override
protected byte[] processAccessoryGetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryGet request: {}", bidibMessage);
byte[] response = null;
try {
AccessoryGetMessage accessoryGetMessage = (AccessoryGetMessage) bidibMessage;
int accessoryNumber = accessoryGetMessage.getAccessoryNumber();
int aspect = 0;
int total = 4;
if (accessoryNumber < 8) {
total = 2;
}
byte[] value = new byte[] { ByteUtils.getLowByte(total) /* total */, 0 /* execute */, 0 /* wait */ };
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
(byte) aspect, value);
response = accessoryStateResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
return response;
}
@Override
protected byte[] processAccessoryGetAllRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryGetAll request: {}", bidibMessage);
byte[] response = null;
try {
Feature featureAccessoryCount = Feature.findFeature(features, BidibLibrary.FEATURE_ACCESSORY_COUNT);
int accessoryCount = featureAccessoryCount.getValue();
int aspect = 0;
for (int accessoryNumber = 0; accessoryNumber < accessoryCount; accessoryNumber++) {
int total = 4;
if (accessoryNumber < 8) {
total = 2;
}
byte[] value = new byte[] { ByteUtils.getLowByte(total) /* total */, 0 /* execute */, 0 /* wait */ };
LOGGER.info("Return data for accessory: {}, data: {}", accessoryNumber, ByteUtils.bytesToHex(value));
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
(byte) aspect, value);
response = accessoryStateResponse.getContent();
sendSpontanousResponse(response);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
return null;
}
@Override
protected byte[] processAccessoryParaGetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryParaGet request: {}", bidibMessage);
byte[] response = null;
try {
AccessoryParaGetMessage accessoryParaGetMessage = (AccessoryParaGetMessage) bidibMessage;
int accessoryNumber = accessoryParaGetMessage.getAccessoryNumber();
int paraNumber = accessoryParaGetMessage.getParaNumber();
if (paraNumber == BidibLibrary.BIDIB_ACCESSORY_SWITCH_TIME) {
byte[] value = new byte[] { ByteUtils.getLowByte(paraNumber) };
paraNumber = BidibLibrary.BIDIB_ACCESSORY_PARA_NOTEXIST;
AccessoryParaResponse accessoryParaResponse =
new AccessoryParaResponse(bidibMessage.getAddr(), getNextSendNum(),
ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(paraNumber), value);
response = accessoryParaResponse.getContent();
}
else {
response = super.processAccessoryParaGetRequest(bidibMessage);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryPara response failed.", ex);
}
return response;
}
//////////
}