strolch-plc/plc-gw-server/src/main/java/li/strolch/plc/gw/server/PlcStateHandler.java

265 lines
9.2 KiB
Java

package li.strolch.plc.gw.server;
import static li.strolch.model.Resource.locatorFor;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.model.builder.BuilderHelper.buildParamName;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.utils.helper.StringHelper.DASH;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator;
import li.strolch.model.ParameterBag;
import li.strolch.model.Resource;
import li.strolch.model.Tags;
import li.strolch.model.builder.ResourceBuilder;
import li.strolch.model.json.ResourceSystemStateFromJson;
import li.strolch.model.log.LogMessage;
import li.strolch.model.log.LogMessageState;
import li.strolch.model.log.LogSeverity;
import li.strolch.model.parameter.StringListParameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.plc.model.ConnectionState;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.privilege.PrivilegedRunnable;
import li.strolch.utils.DataUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PlcStateHandler {
private static final Logger logger = LoggerFactory.getLogger(PlcStateHandler.class);
private final PlcGwServerHandler gwServerHandler;
private final ComponentContainer container;
public PlcStateHandler(PlcGwServerHandler gwServerHandler) {
this.gwServerHandler = gwServerHandler;
this.container = gwServerHandler.getContainer();
}
protected void runAsAgent(PrivilegedRunnable runnable) throws Exception {
this.container.getPrivilegeHandler().runAsAgent(runnable);
}
protected StrolchTransaction openTx(Certificate cert) {
return this.container.getRealm(cert).openTx(cert, getClass(), false);
}
public void handleStillConnected(PlcGwServerHandler.PlcSession plcSession) {
try {
runAsAgent(ctx -> {
try (StrolchTransaction tx = openTx(ctx.getCertificate())) {
// get the gateway and set the state
Resource plc = tx.getResourceBy(TYPE_PLC, plcSession.plcId, false);
if (plc == null) {
plc = buildNewPlc(plcSession, tx);
tx.add(plc);
}
StringParameter stateP = plc.getStringP(PARAM_CONNECTION_STATE);
if (!stateP.getValue().equals(ConnectionState.Connected.name())) {
stateP.setValue(ConnectionState.Connected.name());
plc.getStringP(PARAM_CONNECTION_STATE_MSG).clear();
tx.update(plc);
tx.commitOnClose();
}
}
});
} catch (Exception e) {
logger.error("Failed to handle still connected notification!", e);
}
}
public void handlePlcState(PlcGwServerHandler.PlcSession plcSession, ConnectionState connectionState,
String connectionStateMsg, JsonObject stateJ) {
try {
runAsAgent(ctx -> {
String realm;
ConnectionState existingState;
try (StrolchTransaction tx = openTx(plcSession.certificate)) {
realm = tx.getRealmName();
// get the gateway and set the state
Resource plc = tx.getResourceBy(TYPE_PLC, plcSession.plcId, false);
if (plc == null) {
plc = buildNewPlc(plcSession, tx);
tx.add(plc);
}
StringParameter stateP = plc.getStringP(PARAM_CONNECTION_STATE);
existingState = ConnectionState.valueOf(stateP.getValue());
if (existingState != connectionState) {
stateP.setValue(connectionState.name());
plc.setString(PARAM_CONNECTION_STATE_MSG, connectionStateMsg);
tx.update(plc);
logger.info(
"Updated connection state for PLC " + plc.getId() + " to " + connectionState + (isEmpty(
connectionStateMsg) ? "" : ": " + connectionStateMsg));
}
if (stateJ != null) {
saveGatewayIpAddresses(tx, plc, stateJ.getAsJsonArray(PARAM_IP_ADDRESSES));
saveGatewayVersion(tx, plc, stateJ.getAsJsonObject(PARAM_VERSIONS));
setSystemState(stateJ.getAsJsonObject(PARAM_SYSTEM_STATE), plc);
}
tx.commitOnClose();
}
if (existingState != connectionState) {
if (this.container.hasComponent(OperationsLog.class)) {
OperationsLog operationsLog = this.container.getComponent(OperationsLog.class);
Locator msgLocator = locatorFor(TYPE_PLC, plcSession.plcId).append(
ConnectionState.class.getSimpleName());
operationsLog.updateState(realm, msgLocator, LogMessageState.Inactive);
if (connectionState == ConnectionState.Connected) {
operationsLog.addMessage(
new LogMessage(realm, plcSession.plcId, msgLocator, LogSeverity.Info,
LogMessageState.Information, PlcGwSrvI18n.bundle,
"execution.plc.connected").value("plc", plcSession.plcId));
} else {
operationsLog.addMessage(
new LogMessage(realm, plcSession.plcId, msgLocator, LogSeverity.Error,
LogMessageState.Active, PlcGwSrvI18n.bundle,
"execution.plc.connectionLost").value("plc", plcSession.plcId));
}
}
this.gwServerHandler.notifyConnectionState(plcSession.plcId, connectionState);
}
});
} catch (Exception e) {
logger.error("Failed to handle gateway connection state notification!", e);
}
}
private void saveGatewayIpAddresses(StrolchTransaction tx, Resource plc, JsonArray ipAddresses) {
if (ipAddresses.size() == 0)
return;
// update local IPs
StringListParameter localIpP = plc.getParameter(PARAM_LOCAL_IP, true);
List<String> ipAddressesS = StreamSupport.stream(ipAddresses.spliterator(), false).map(e -> {
JsonObject jsonObject = e.getAsJsonObject();
String ip = jsonObject.getAsJsonPrimitive(PARAM_IP_ADDRESS).getAsString();
String hostname = jsonObject.getAsJsonPrimitive(PARAM_HOST_NAME).getAsString();
String mac = jsonObject.getAsJsonPrimitive(PARAM_MAC_ADDRESS).getAsString();
return ip + " | " + hostname + " | " + mac;
}).collect(Collectors.toList());
if (!localIpP.getValue().equals(ipAddressesS)) {
localIpP.setValue(ipAddressesS);
tx.update(plc);
}
}
private void saveGatewayVersion(StrolchTransaction tx, Resource plc, JsonObject versions) {
if (versions == null || versions.isJsonNull())
return;
if (versions.has(AGENT_VERSION)) {
JsonObject agentVersion = versions.get(AGENT_VERSION).getAsJsonObject();
ParameterBag bag = updateVersionParams(plc, AGENT_VERSION, "Agent Version", agentVersion);
setOrAdd(bag, versions, AGENT_NAME, "Agent Name");
tx.update(plc);
}
if (versions.has(APP_VERSION)) {
JsonObject appVersion = versions.get(APP_VERSION).getAsJsonObject();
updateVersionParams(plc, APP_VERSION, "App Version", appVersion);
tx.update(plc);
}
if (versions.has(COMPONENT_VERSIONS)) {
JsonArray componentVersions = versions.get(COMPONENT_VERSIONS).getAsJsonArray();
componentVersions.forEach(e -> {
JsonObject componentVersion = e.getAsJsonObject();
String componentName = componentVersion.get(COMPONENT_NAME).getAsString();
updateVersionParams(plc, componentName, "Component " + componentName, componentVersion);
tx.update(plc);
});
}
}
private ParameterBag updateVersionParams(Resource plc, String bagKey, String bagName, JsonObject version) {
ParameterBag bag = plc.getParameterBag(bagKey);
if (bag == null) {
bag = new ParameterBag(bagKey, bagName, Tags.VERSION);
plc.addParameterBag(bag);
}
setOrAdd(bag, version, BUILD_TIMESTAMP, "Build timestamp");
setOrAdd(bag, version, SCM_BRANCH, "SCM Branch");
setOrAdd(bag, version, SCM_REVISION, "SCM Revision");
StringParameter versionP = setOrAdd(bag, version, ARTIFACT_VERSION, "Artifact Version");
versionP.setInterpretation("Version");
versionP.setUom("Version");
setOrAdd(bag, version, ARTIFACT_ID, "Artifact ID");
setOrAdd(bag, version, GROUP_ID, "Group ID");
return bag;
}
private StringParameter setOrAdd(ParameterBag bag, JsonObject version, String paramKey, String paramName) {
String value = version.has(paramKey) ? version.get(paramKey).getAsString() : DASH;
StringParameter param = bag.getParameter(paramKey);
if (param == null) {
param = new StringParameter(paramKey, paramName, value);
bag.addParameter(param);
} else {
param.setValue(value);
}
return param;
}
private Resource buildNewPlc(PlcGwServerHandler.PlcSession plcSession, StrolchTransaction tx) {
return new ResourceBuilder(plcSession.plcId, plcSession.plcId, TYPE_PLC) //
.defaultBag() //
.string(PARAM_CONNECTION_STATE, buildParamName(PARAM_CONNECTION_STATE))
.enumeration(ConnectionState.Disconnected)
.end() //
.string(PARAM_CONNECTION_STATE_MSG, buildParamName(PARAM_CONNECTION_STATE_MSG))
.end() //
.stringList(PARAM_LOCAL_IP, buildParamName(PARAM_LOCAL_IP))
.end() //
.endBag() //
.build();
}
private void setSystemState(JsonObject systemState, Resource gateway) {
if (systemState == null)
return;
new ResourceSystemStateFromJson().compactStates() //
// os
.withSystemLoadAverageState() //
// memory
.withMemoryRounding(DataUnit.MegaBytes) //
.withFreePhysicalMemorySizeState() //
// storage
.withStorageSpaceRounding(DataUnit.GigaBytes) //
.withFreeSpaceState() //
.fillElement(systemState, gateway);
}
}