diff --git a/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemory.java b/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemory.java new file mode 100644 index 000000000..9484dea9e --- /dev/null +++ b/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemory.java @@ -0,0 +1,88 @@ +package club.joylink.rtss.iscs.proto.model; + +import club.joylink.iscs.model.data.DataModelMapProto; +import club.joylink.iscs.model.data.DataModelProto; +import com.google.protobuf.GeneratedMessageV3; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.lang.reflect.Method; +import java.util.Map; + +public interface ModelMemory { + + /** + * 获取模型数据的引用 + */ + DataModelProto.ModelRef.Builder refFrom(GeneratedMessageV3 model) throws Exception; + + /** + * 获取引用引用的模型实例数据 + */ + T modelFrom(DataModelProto.ModelRef.Builder ref) throws Exception; + + /** + * 获取引用引用的模型实例数据 + */ + T modelFrom(DataModelProto.ModelRef ref) throws Exception; + + /** + * 获取所有模型数据的副本 + */ + DataModelMapProto.DataModelMap allDataModels(); + + /** + * 从已有数据恢复模型数据 + */ + void recoverFrom(DataModelMapProto.DataModelMap modelsData); + + /** + * 从已有数据恢复模型数据 + */ + void recoverFrom(byte[] modelsData) throws InvalidProtocolBufferException; + + public static ModelMemory create(byte[] modelsData) throws InvalidProtocolBufferException { + ModelMemoryImpl impl = new ModelMemoryImpl(); + impl.recoverFrom(modelsData); + return impl; + } + + /** + * 获取模型的身份信息 + */ + public static DataModelProto.DataModel identityFrom(GeneratedMessageV3 model) throws Exception { + //getIdentity + Method getIdentity = getDeclaredMethod(model.getClass(), "getIdentity"); + if (null != getIdentity) { + return (DataModelProto.DataModel) getIdentity.invoke(model); + } + //遍历 + final Method[] mds = model.getClass().getMethods(); + int count = 0; + for (Method md : mds) { + if (DataModelProto.DataModel.class.equals(md.getReturnType())) { + getIdentity = md; + count++; + } + } + if (count > 1) { + throw new Exception(String.format("%s 中 表示身份信息DataModelProto.DataModel有多个,可以将其中一个命名为'identity'表示身份信息", model.getClass().getSimpleName())); + } + if (count < 1) { + throw new Exception(String.format("%s 中 没有表示身份信息DataModelProto.DataModel,可以添加一个命名为'identity'", model.getClass().getSimpleName())); + } + return (DataModelProto.DataModel) getIdentity.invoke(model); + } + + /** + * 根据模型类型获取该类模型的集合,可以通过遍历该集合来遍历该类的所有模型数据 + */ + public Map getModelMap(Class modelClass) throws Exception; + + static Method getDeclaredMethod(Class cls, String name, Class... parameterTypes) { + try { + return cls.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + } + return null; + } +} diff --git a/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemoryImpl.java b/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemoryImpl.java new file mode 100644 index 000000000..92395e821 --- /dev/null +++ b/src/main/java/club/joylink/rtss/iscs/proto/model/ModelMemoryImpl.java @@ -0,0 +1,203 @@ +package club.joylink.rtss.iscs.proto.model; + +import club.joylink.iscs.model.data.DataModelMapProto; +import club.joylink.iscs.model.data.DataModelProto; +import com.google.protobuf.GeneratedMessageV3; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * 模型数据存储实现 + */ +@Slf4j +public class ModelMemoryImpl implements ModelMemory { + + /** + * 模型数据存储空间 + */ + private DataModelMapProto.DataModelMap.Builder dmmBuilder = DataModelMapProto.DataModelMap.newBuilder(); + /** + * 模型分类查找实现 + */ + private final Map refMethodMap = new HashMap<>(); + /** + * 快速存储模型数据实现 + */ + private final Map, Method> putMethodMap = new HashMap<>(); + /** + * 快速通过模型类获取模型的集合 + */ + private final Map, Method> modelMap = new HashMap<>(); + + public ModelMemoryImpl() { + + } + + /** + * 获取模型数据的引用 + */ + public synchronized DataModelProto.ModelRef.Builder refFrom(GeneratedMessageV3 model) throws Exception { + final DataModelProto.DataModel identify = ModelMemory.identityFrom(model); + this.putModelData(identify, model); + return DataModelProto.ModelRef.newBuilder().setType(identify.getType()).setId(identify.getId()); + } + + /** + * 获取引用引用的模型实例数据 + */ + public synchronized T modelFrom(DataModelProto.ModelRef.Builder ref) throws Exception { + return (T) findFromMap(ref.build()); + } + + /** + * 获取引用引用的模型实例数据 + */ + public synchronized T modelFrom(DataModelProto.ModelRef ref) throws Exception { + return (T) findFromMap(ref); + } + + /** + * 获取所有模型数据的副本 + */ + public synchronized DataModelMapProto.DataModelMap allDataModels() { + return this.dmmBuilder.build(); + } + + /** + * 从已有数据恢复模型数据 + */ + public synchronized void recoverFrom(DataModelMapProto.DataModelMap modelsData) { + this.dmmBuilder = DataModelMapProto.DataModelMap.newBuilder(modelsData); + } + + /** + * 从已有数据恢复模型数据 + */ + public synchronized void recoverFrom(byte[] modelsData) throws InvalidProtocolBufferException { + this.dmmBuilder = DataModelMapProto.DataModelMap.newBuilder(DataModelMapProto.DataModelMap.parseFrom(modelsData)); + } + + /** + * 根据模型类型获取该类模型的集合,可以通过遍历该集合来遍历该类的所有模型数据 + */ + public synchronized Map getModelMap(Class modelClass) throws Exception { + final Map rt = doFindByModelClass(modelClass); + if (null != rt) { + return rt; + } + //遍历所有集合查找 + Method[] mds = dmmBuilder.getClass().getMethods(); + for (Method md : mds) { + final String mdName = md.getName(); + if (mdName.startsWith("get") && mdName.endsWith("Map")) { + final Map map = (Map) md.invoke(dmmBuilder); + for (Object v : map.values()) { + if (v.getClass().equals(modelClass)) { + this.modelMap.put(modelClass, md); + show("==>>遍历获取模型的集合:modelClass = " + modelClass.getSimpleName()); + return map; + } + } + } + } + return new HashMap<>(); + } + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * 存储实例 + */ + private void putModelData(final DataModelProto.DataModel identify, final GeneratedMessageV3 model) throws Exception { + final Method putModelMethod = this.putMethodMap.get(model.getClass()); + if (null != putModelMethod) { + putModelMethod.invoke(this.dmmBuilder, identify.getId(), model); + show("==>>存储方法存在,直接存储:type = " + identify.getType() + " code = " + identify.getId()); + } else { + final Method[] putMds = this.dmmBuilder.getClass().getMethods(); + for (Method putMd : putMds) { + final String putMdName = putMd.getName(); + if (putMdName.startsWith("put")) { + final Class[] pts = putMd.getParameterTypes(); + if (null != pts && pts.length == 2) { + if (pts[0].equals(String.class) && pts[1].equals(model.getClass())) { + if (!this.putMethodMap.containsKey(model.getClass())) { + this.putMethodMap.put(model.getClass(), putMd); + } + putMd.invoke(this.dmmBuilder, identify.getId(), model); + show("==>>存储方法不存在,储遍历存储:type = " + identify.getType() + " code = " + identify.getId()); + break; + } + } + } + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + private GeneratedMessageV3 findFromMap(DataModelProto.ModelRef ref) throws Exception { + return this.doFindFromMap(ref); + } + + private Map doFindByModelClass(Class modelClass) throws Exception { + Method md = this.modelMap.get(modelClass); + if (null != md) { + final Map map = (Map) md.invoke(dmmBuilder); + show("==>>直接获取模型的集合:modelClass = " + modelClass.getSimpleName()); + return map; + } + return null; + } + + /** + * 遍历所有map来查找ref对应的model实例 + */ + private GeneratedMessageV3 doFindFromMap(DataModelProto.ModelRef ref) throws Exception { + //优先通过类型来查找 + GeneratedMessageV3 model = this.doFindByType(ref); + if (null != model) { + show("==>>优先通过类型找到:type = " + ref.getType() + " code = " + ref.getId()); + return model; + } + //遍历所有集合查找 + Method[] mds = dmmBuilder.getClass().getMethods(); + for (Method md : mds) { + final String mdName = md.getName(); + if (mdName.startsWith("get") && mdName.endsWith("Map")) { + Map map = (Map) md.invoke(dmmBuilder); + model = (GeneratedMessageV3) map.get(ref.getId()); + if (null != model) { + if (!DataModelProto.ModelType.Any.equals(ref.getType())) { + if (!refMethodMap.containsKey(ref.getType())) { + refMethodMap.put(ref.getType(), md); + show("==>>保存类型查找信息:type = " + ref.getType() + " getMapMethod = " + mdName); + } + } + show("==>>通过遍历所有集合找到:type = " + ref.getType() + " code = " + ref.getId()); + return model; + } + } + } + return null; + } + + /** + * 通过类型来查找模型实例 + */ + private GeneratedMessageV3 doFindByType(DataModelProto.ModelRef ref) throws Exception { + //getXXXMap() + final Method getMapMethod = refMethodMap.get(ref.getType()); + if (null != getMapMethod) { + final Map map = (Map) getMapMethod.invoke(dmmBuilder); + return (GeneratedMessageV3) map.get(ref.getId()); + } + return null; + } + + private static void show(String msg) { + log.debug(msg); + } +} diff --git a/src/main/java/club/joylink/rtss/iscs/services/IscsModelMemoryService.java b/src/main/java/club/joylink/rtss/iscs/services/IscsModelMemoryService.java new file mode 100644 index 000000000..1bd65045f --- /dev/null +++ b/src/main/java/club/joylink/rtss/iscs/services/IscsModelMemoryService.java @@ -0,0 +1,115 @@ +package club.joylink.rtss.iscs.services; + +import club.joylink.rtss.iscs.dao.IscsModelDataDAO; +import club.joylink.rtss.iscs.entity.IscsModelData; +import club.joylink.rtss.iscs.entity.IscsModelDataExample; +import club.joylink.rtss.iscs.proto.model.ModelMemory; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * iscs模型使用业务实现
+ * iscs的所有页面的模型数据 + */ +@Service +public class IscsModelMemoryService { + @Autowired + private IscsModelDataDAO modelDataDao; + /** + * key : iscs的类型
+ * value : iscs的所有页面的模型数据
+ */ + private final Map> modelMemoryMap = new ConcurrentHashMap<>(); + + /** + * iscs启动时加载模型数据 + */ + @Transactional(readOnly = true) + public void loadModelData() throws Exception { + final List mmiList = this.findAllModelMemoryIndex(ISCS_DEFAULT_TYPE); + for (ModelMemoryIndex mmi : mmiList) { + final Map map = getIscsModelMap(mmi.type); + map.put(mmi, this.loadModelMemory(mmi)); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + private synchronized Map getIscsModelMap(String iscsType) { + Map map = modelMemoryMap.get(iscsType); + if (null == map) { + map = new ConcurrentHashMap<>(); + modelMemoryMap.put(iscsType, map); + } + return map; + } + + private List findAllModelMemoryIndex(String iscsType) { + final List rt = new ArrayList<>(); + // + final IscsModelDataExample example = new IscsModelDataExample(); + final List list = modelDataDao.selectByExample(example); + if (null != list) { + list.forEach(md -> { + rt.add(new ModelMemoryIndex(md.getSystem(), md.getView(), md.getPlace())); + }); + } + return rt; + } + + private ModelMemory loadModelMemory(ModelMemoryIndex mmi) throws Exception { + IscsModelDataExample example = new IscsModelDataExample(); + example.createCriteria().andSystemEqualTo(mmi.system).andViewEqualTo(mmi.view).andPlaceEqualTo(mmi.place); + List list = modelDataDao.selectByExampleWithBLOBs(example); + return ModelMemory.create(list.get(0).getData()); + } + /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * 模型数据的索引(页面id) + */ + @Getter + @Setter + public static class ModelMemoryIndex { + //iscs 类型(扩展用,暂时默认值) + private String type = ISCS_DEFAULT_TYPE; + //iscs 系统(一级菜单) + private String system; + //iscs 视图(二级菜单) + private String view; + //iscs 场所(车站、变电所,等)(三级菜单) + private String place; + + public ModelMemoryIndex() { + } + + public ModelMemoryIndex(String system, String view, String place) { + this.system = system; + this.view = view; + this.place = place; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModelMemoryIndex that = (ModelMemoryIndex) o; + return type.equals(that.type) && system.equals(that.system) && view.equals(that.view) && place.equals(that.place); + } + + @Override + public int hashCode() { + return Objects.hash(type, system, view, place); + } + } + + public static final String ISCS_DEFAULT_TYPE = "FAST_TRAIN"; +}