745 lines
31 KiB
C#
745 lines
31 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.View;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Windows.Input;
|
|
using System.Windows.Threading;
|
|
using static AuroraPLC.PLCBase;
|
|
|
|
namespace AuroraPLC.Views
|
|
{
|
|
public class WordValue : NotifyPropertyChangedBase
|
|
{
|
|
private string _word;
|
|
public string Word {
|
|
get { return _word; }
|
|
set {
|
|
if (_word != value) {
|
|
_word = value;
|
|
NotifyPropertyChanged("Word");
|
|
}
|
|
}
|
|
}
|
|
public string Address { get; set; }
|
|
private string _description;
|
|
public string Description {
|
|
get => _description;
|
|
set {
|
|
if (_description != value) {
|
|
_description = value;
|
|
NotifyPropertyChanged("Description");
|
|
}
|
|
}
|
|
}
|
|
public int addrOffset { get; set; }
|
|
|
|
private bool _use = true;
|
|
public bool Use {
|
|
get { return _use; }
|
|
set { SetProperty(ref _use, value); }
|
|
} // DataGridRow의 IsEnable과 Binding되는 property
|
|
}
|
|
|
|
public class UCPLCWordViewModel : NotifyPropertyChangedBase
|
|
{
|
|
Logger Log = new Logger("PLC Mon. Word");
|
|
|
|
// 32bit 옵션을 지원하기 위해서 (아스키 포함) string 으로 변경...
|
|
private string _WORD_DATA;
|
|
public string WORD_DATA { get => _WORD_DATA; set => SetProperty(ref _WORD_DATA, value); }
|
|
|
|
public ICommand CMD_VisibleChanged { get; }
|
|
//public ICommand CMD_CalculateFlatness { get; }
|
|
public ICommand CMD_ChangeValue { get; }
|
|
|
|
public List<WordValue> DigitalInputs { get; set; } = new List<WordValue>();
|
|
public List<WordValue> DigitalOutputs { get; set; } = new List<WordValue>();
|
|
public List<string> DataViewTypeList { get; set; }
|
|
= new List<string>() { PLCBase.DisplayFormat.Bit16, PLCBase.DisplayFormat.Bit32Odd, PLCBase.DisplayFormat.Bit32Even, PLCBase.DisplayFormat.ASCII };
|
|
public string DataViewTypeInput { get; set; }
|
|
public string DataViewTypeOutput { get; set; }
|
|
private bool _isCheckedDataViewHexInput = false;
|
|
public bool IsCheckedDataViewHexInput { get => _isCheckedDataViewHexInput; set => SetProperty(ref _isCheckedDataViewHexInput, value); }
|
|
|
|
private bool _isCheckedDataViewHexOutput = false;
|
|
public bool IsCheckedDataViewHexOutput { get => _isCheckedDataViewHexOutput; set => SetProperty(ref _isCheckedDataViewHexOutput, value); }
|
|
|
|
|
|
//------------------------
|
|
// For Sim Only
|
|
public bool IsThisInput { get; private set; }
|
|
|
|
// Input/Ouput 공용 Item
|
|
private WordValue selectedWordItem;
|
|
public WordValue SelectedWordItem {
|
|
get {
|
|
return selectedWordItem;
|
|
}
|
|
set {
|
|
selectedWordItem = value;
|
|
}
|
|
}
|
|
|
|
private WordValue selectedInputItem;
|
|
public WordValue SelectedInputItem {
|
|
get {
|
|
return selectedInputItem;
|
|
}
|
|
set {
|
|
// 반대편 datagrid 의 선택을 해제한다.
|
|
selectedOutputItem = null;
|
|
NotifyPropertyChanged("SelectedOutputItem");
|
|
//=====================================
|
|
selectedInputItem = value;
|
|
IsThisInput = true;
|
|
selectedWordItem = value; // For Sim mode : Input bit change
|
|
NotifyPropertyChanged("SelectedWordAddress");
|
|
}
|
|
}
|
|
//====================================
|
|
private WordValue selectedOutputItem;
|
|
public WordValue SelectedOutputItem {
|
|
get {
|
|
return selectedOutputItem;
|
|
}
|
|
set {
|
|
// 반대편 datagrid 의 선택을 해제한다.
|
|
selectedInputItem = null;
|
|
NotifyPropertyChanged("SelectedInputItem");
|
|
//=====================================
|
|
selectedOutputItem = value;
|
|
IsThisInput = false;
|
|
selectedWordItem = value; // For Sim mode (Input 쪽과 일관성을 위해서)
|
|
NotifyPropertyChanged("SelectedWordAddress");
|
|
}
|
|
}
|
|
|
|
public string SelectedWordAddress {
|
|
get {
|
|
return SelectedWordItem?.Address;
|
|
}
|
|
}
|
|
|
|
private int selectedInputIndex = 0;
|
|
public int SelectedInputIndex {
|
|
get => selectedInputIndex;
|
|
set {
|
|
if (selectedInputIndex != value) {
|
|
selectedInputIndex = value;
|
|
DigitalInputs = InputMaps[value];
|
|
NotifyPropertyChanged("DigitalInputs");
|
|
}
|
|
}
|
|
}
|
|
|
|
private int selectedOutputIndex = 0;
|
|
public int SelectedOutputIndex {
|
|
get => selectedOutputIndex;
|
|
set {
|
|
if (selectedOutputIndex != value) {
|
|
selectedOutputIndex = value;
|
|
DigitalOutputs = OutputMaps[value];
|
|
NotifyPropertyChanged("DigitalOutputs");
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool isActivated = false;
|
|
public bool IsActivated {
|
|
get => isActivated;
|
|
set {
|
|
if (isActivated != value) {
|
|
isActivated = value;
|
|
NotifyPropertyChanged("IsActivated");
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool _changeButtonEnable;
|
|
public bool ChangeButtonEnable {
|
|
get => _changeButtonEnable;
|
|
set => SetProperty(ref _changeButtonEnable, value);
|
|
}
|
|
|
|
public Dictionary<int, List<WordValue>> InputMaps = new Dictionary<int, List<WordValue>>();
|
|
public Dictionary<int, List<WordValue>> OutputMaps = new Dictionary<int, List<WordValue>>();
|
|
|
|
private bool isVisible;
|
|
private eLangType curLanguage;
|
|
|
|
private IDeviceEntry DeviceManager;
|
|
private readonly PLCBase _plc;
|
|
private ePLCDevice _deviceName;
|
|
|
|
private readonly DispatcherTimer MonitorTimer;
|
|
|
|
public UCPLCWordViewModel(IAuroraEnvironment aurora, string deviceId, ePLCNetType plcNetType, ePLCDevice device = ePLCDevice.W)
|
|
{
|
|
DeviceManager = aurora.DeviceManager;
|
|
|
|
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
|
|
CMD_ChangeValue = new DelegateCommand(ChangeWordValue);
|
|
_deviceName = device;
|
|
|
|
_plc = DeviceManager[deviceId] as PLCBase;
|
|
|
|
List<WordValue> input = new List<WordValue>();
|
|
List<WordValue> output = new List<WordValue>();
|
|
InputMaps.Add(0, input);
|
|
OutputMaps.Add(0, output);
|
|
|
|
isVisible = false;
|
|
curLanguage = PLCBase.Language;
|
|
|
|
// Config 로부터 Input/Output 메모리 맵 파싱 및 할당
|
|
LoadWordFromConfig();
|
|
|
|
Observable.Merge(aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_ACTIVATED),
|
|
aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_DEACTIVATED))
|
|
.Select(msg => msg.Param as string)
|
|
.Where(x => x == deviceId).ObserveOnDispatcher()
|
|
.Subscribe(x => OnDeviceActiveChanged());
|
|
|
|
// Log In/Out 시점 파악을 위한 이벤트 관측하기 : For Write 권한 제어
|
|
aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_PERMISSION_CHANGED)
|
|
.Subscribe(x => UpdateWriteState());
|
|
|
|
// 언어 변경 이벤트
|
|
aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_LANGUAGE_CHANGED)
|
|
.Select(msg => msg.Param.ToString().Split('-').First().ToLower())//.Where(lan => lan != _currLanguage)
|
|
.ObserveOnDispatcher().Subscribe(lan => UpdateLanguage(lan));
|
|
|
|
DigitalInputs = InputMaps[0];
|
|
DigitalOutputs = OutputMaps[0];
|
|
|
|
// display 포맷 옵션 초기화
|
|
DataViewTypeInput = PLCBase.DisplayFormat.Bit16;
|
|
DataViewTypeOutput = PLCBase.DisplayFormat.Bit16;
|
|
|
|
MonitorTimer = new DispatcherTimer();
|
|
MonitorTimer.Tick += OnMonitoring;
|
|
MonitorTimer.Interval = TimeSpan.FromMilliseconds(250);
|
|
}
|
|
|
|
private void UpdateLanguage(string lan)
|
|
{
|
|
// 예외 조건
|
|
// 언어가 변경되었는데, 모니터링 뷰 화면이 아닌 경우,
|
|
if (isVisible == false) {
|
|
|
|
} else { // 모니터링 화면인 경우,
|
|
// 타이머 중단
|
|
MonitorTimer.Stop();
|
|
|
|
switch (lan) {
|
|
case "en": {
|
|
UpdateViewLanguage(eLangType.English);
|
|
|
|
break;
|
|
}
|
|
|
|
case "ko": {
|
|
UpdateViewLanguage(eLangType.Korean);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 타이머 재시작
|
|
MonitorTimer.Start();
|
|
}
|
|
}
|
|
|
|
private void UpdateViewLanguage(eLangType langType)
|
|
{
|
|
int i = 0;
|
|
switch (langType) {
|
|
case eLangType.English: {
|
|
foreach (var ioValue in DigitalInputs) {
|
|
if (_deviceName == ePLCDevice.D) {
|
|
ioValue.Description = _plc.DWordInputs[i].description.Comment_EN;
|
|
} else {
|
|
ioValue.Description = _plc.WordInputs[i].description.Comment_EN;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
i = 0;
|
|
foreach (var ioValue in DigitalOutputs) {
|
|
if (_deviceName == ePLCDevice.D) {
|
|
ioValue.Description = _plc.DWordOutputs[i].description.Comment_EN;
|
|
} else {
|
|
ioValue.Description = _plc.WordOutputs[i].description.Comment_EN;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case eLangType.Korean: {
|
|
foreach (var ioValue in DigitalInputs) {
|
|
if (_deviceName == ePLCDevice.D) {
|
|
ioValue.Description = _plc.DWordInputs[i].description.Comment_KR;
|
|
} else {
|
|
ioValue.Description = _plc.WordInputs[i].description.Comment_KR;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
i = 0;
|
|
foreach (var ioValue in DigitalOutputs) {
|
|
if (_deviceName == ePLCDevice.D) {
|
|
ioValue.Description = _plc.DWordOutputs[i].description.Comment_KR;
|
|
} else {
|
|
ioValue.Description = _plc.WordOutputs[i].description.Comment_KR;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 뷰모델 및 PLC 언어도 업데이트
|
|
PLCBase.Language = langType;
|
|
curLanguage = langType;
|
|
}
|
|
|
|
private void UpdateWriteState() // 화면 내에서 사용자 권한이 변경되었을 경우
|
|
{
|
|
/*
|
|
var curPermissionLevel = ApplicationExt.PermissionLevel;
|
|
if (curPermissionLevel > (ushort)Aurora.Api.PermissionLevel.LEVEL3 || IsActivated == false) { // 권한 부족
|
|
ChangeButtonEnable = false;
|
|
} else {
|
|
if (PLC.IsViewEnabled) {
|
|
ChangeButtonEnable = true;
|
|
} else {
|
|
ChangeButtonEnable = false;
|
|
}
|
|
}
|
|
*/
|
|
// Auto 모드인경우, Write 불가
|
|
if (PLCStatic.IsViewEnabled) {
|
|
ChangeButtonEnable = true;
|
|
} else {
|
|
ChangeButtonEnable = false;
|
|
}
|
|
}
|
|
|
|
private void VisibleChanged(object obj) // 화면 전환 시 동작하는 곳
|
|
{
|
|
UpdateWriteState();
|
|
|
|
isVisible = (bool)obj;
|
|
if (isVisible) {
|
|
// 뷰모델이 가지고 있는 언어와 현재 Aurora 언어가 다르면, 설명 언어를 변경함
|
|
if (curLanguage != _plc.GetLangTypeFrom(ApplicationExt.CurLang)) {
|
|
UpdateViewLanguage(PLCBase.Language);
|
|
}
|
|
|
|
MonitorTimer.Start();
|
|
} else {
|
|
MonitorTimer.Stop();
|
|
}
|
|
}
|
|
|
|
private void OnDeviceActiveChanged()
|
|
{
|
|
IsActivated = _plc.IsActivated;
|
|
if (IsActivated == false) {
|
|
ChangeButtonEnable = false;
|
|
}
|
|
}
|
|
|
|
private void LoadWordFromConfig()
|
|
{
|
|
if(_deviceName == ePLCDevice.D) {
|
|
// Word Input
|
|
var startAddress = _plc.DWordInputs.Count != 0 ? _plc.DWordInputs.OrderBy(x => x.address).First().address : 0;
|
|
foreach (var inputW in _plc.DWordInputs) {
|
|
// 주소와 설명문으로 파싱
|
|
//var (address, desc) = inputW;
|
|
var address = inputW.Item1;
|
|
string desc = null;
|
|
switch (curLanguage) {
|
|
case eLangType.English: {
|
|
desc = inputW.Item2.Comment_EN;
|
|
|
|
break;
|
|
}
|
|
case eLangType.Korean: {
|
|
desc = inputW.Item2.Comment_KR;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//desc.Trim();
|
|
desc?.Trim();
|
|
|
|
List<WordValue> InPutlist = InputMaps[0];
|
|
|
|
string strAddress = "D" + address.ToString(); // convert to Hex string (4 자리)
|
|
var ioValue = new WordValue() {
|
|
Address = strAddress,
|
|
Description = desc,
|
|
//hexAddress = strAddress.Substring(1, strAddress.Length - 1),
|
|
addrOffset = address - startAddress,
|
|
Use = true // 특정 Value를 사용하지 않을 경우 false로 설정 (DataGridRow의 IsEnabled와 연동)
|
|
};
|
|
|
|
// 저장
|
|
//InPutlist.AddRange(TempInPutlist);
|
|
InPutlist.Add(ioValue);
|
|
}
|
|
|
|
// Word Output
|
|
startAddress = _plc.DWordOutputs.Count != 0 ? _plc.DWordOutputs.OrderBy(x => x.address).First().address : 0;
|
|
foreach (var outputW in _plc.DWordOutputs) {
|
|
// 주소와 설명문으로 파싱
|
|
//var (address, desc) = outputW;
|
|
var address = outputW.Item1;
|
|
string desc = null;
|
|
switch (curLanguage) {
|
|
case eLangType.English: {
|
|
desc = outputW.Item2.Comment_EN;
|
|
|
|
break;
|
|
}
|
|
case eLangType.Korean: {
|
|
desc = outputW.Item2.Comment_KR;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
desc?.Trim();
|
|
|
|
List<WordValue> OutPutlist = OutputMaps[0];
|
|
|
|
string strAddress = "D" + address.ToString(); // convert to Hex string (4자리)
|
|
|
|
var ioValue = new WordValue() {
|
|
Address = strAddress,
|
|
Description = desc,
|
|
//hexAddress = strAddress.Substring(1, strAddress.Length - 1),
|
|
addrOffset = address - startAddress,
|
|
Use = true // 특정 Value를 사용하지 않을 경우 false로 설정 (DataGridRow의 IsEnabled와 연동)
|
|
};
|
|
|
|
// 저장
|
|
//InPutlist.AddRange(TempInPutlist);
|
|
OutPutlist.Add(ioValue);
|
|
}
|
|
} else {
|
|
// Word Input
|
|
var startAddress = _plc.WordInputs.Count != 0 ? _plc.WordInputs.OrderBy(x => x.address).First().address : 0;
|
|
foreach (var inputW in _plc.WordInputs) {
|
|
// 주소와 설명문으로 파싱
|
|
//var (address, desc) = inputW;
|
|
var address = inputW.Item1;
|
|
string desc = null;
|
|
switch (curLanguage) {
|
|
case eLangType.English: {
|
|
desc = inputW.Item2.Comment_EN;
|
|
|
|
break;
|
|
}
|
|
case eLangType.Korean: {
|
|
desc = inputW.Item2.Comment_KR;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//desc.Trim();
|
|
desc?.Trim();
|
|
|
|
List<WordValue> InPutlist = InputMaps[0];
|
|
|
|
string strAddress = "W" + address.ToString("X4"); // convert to Hex string (4 자리)
|
|
var ioValue = new WordValue() {
|
|
Address = strAddress,
|
|
Description = desc,
|
|
//hexAddress = strAddress.Substring(1, strAddress.Length - 1),
|
|
addrOffset = address - startAddress,
|
|
Use = true // 특정 Value를 사용하지 않을 경우 false로 설정 (DataGridRow의 IsEnabled와 연동)
|
|
};
|
|
|
|
// 저장
|
|
//InPutlist.AddRange(TempInPutlist);
|
|
InPutlist.Add(ioValue);
|
|
}
|
|
|
|
// Word Output
|
|
startAddress = _plc.WordOutputs.Count != 0 ? _plc.WordOutputs.OrderBy(x => x.address).First().address : 0;
|
|
foreach (var outputW in _plc.WordOutputs) {
|
|
// 주소와 설명문으로 파싱
|
|
//var (address, desc) = outputW;
|
|
var address = outputW.Item1;
|
|
string desc = null;
|
|
switch (curLanguage) {
|
|
case eLangType.English: {
|
|
desc = outputW.Item2.Comment_EN;
|
|
|
|
break;
|
|
}
|
|
case eLangType.Korean: {
|
|
desc = outputW.Item2.Comment_KR;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
desc?.Trim();
|
|
|
|
List<WordValue> OutPutlist = OutputMaps[0];
|
|
|
|
string strAddress = "W" + address.ToString("X4"); // convert to Hex string (4자리)
|
|
|
|
var ioValue = new WordValue() {
|
|
Address = strAddress,
|
|
Description = desc,
|
|
//hexAddress = strAddress.Substring(1, strAddress.Length - 1),
|
|
addrOffset = address - startAddress,
|
|
Use = true // 특정 Value를 사용하지 않을 경우 false로 설정 (DataGridRow의 IsEnabled와 연동)
|
|
};
|
|
|
|
// 저장
|
|
//InPutlist.AddRange(TempInPutlist);
|
|
OutPutlist.Add(ioValue);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void OnMonitoring(object sender, EventArgs e)
|
|
{
|
|
if (!_plc.IsActivated) {
|
|
return;
|
|
}
|
|
|
|
// 뷰 상태 모니터링 : write 버튼 비활성화
|
|
UpdateWriteState();
|
|
|
|
|
|
DataViewTypePLCMonitoring(DigitalInputs, true);
|
|
DataViewTypePLCMonitoring(DigitalOutputs, false);
|
|
}
|
|
|
|
private void DataViewTypePLCMonitoring(List<WordValue> IOList, bool IsInput)
|
|
{
|
|
int index = 0;
|
|
if (IOList.Count <= 0) {
|
|
return;
|
|
}
|
|
var memAddress = IOList[0].addrOffset;
|
|
|
|
(int err, short[] data) res;
|
|
if (_deviceName == ePLCDevice.D) {
|
|
res = _plc.ReadWordBlock(memAddress, IOList.Count(), ePLCDevice.D, isInput: IsInput);
|
|
} else {
|
|
res = _plc.ReadWordBlock(memAddress, IOList.Count(), isInput: IsInput);
|
|
}
|
|
|
|
if(res.data == null) {
|
|
return;
|
|
}
|
|
|
|
string dataViewType = IsInput ? DataViewTypeInput : DataViewTypeOutput;
|
|
bool hexType = IsInput ? IsCheckedDataViewHexInput : IsCheckedDataViewHexOutput;
|
|
|
|
// LUKAS_TEST
|
|
string tempStrComp = "";
|
|
try {
|
|
foreach (var ioValue in IOList) {
|
|
switch (dataViewType) {
|
|
case PLCBase.DisplayFormat.Bit32Odd:
|
|
if (index % 2 == 1) {
|
|
ioValue.Use = true;
|
|
// 메모리 주소가 홀수 - 짝수로 쌍을 이룸
|
|
// 마지막 주소가 버퍼 범위를 벗어나는지 검사 필요..
|
|
// 미츠비시 PLC 는 big endigan 임. lower word -> upper word 순임
|
|
if (index + 1 < res.data.Count()) {
|
|
tempStrComp = _plc.Conv_ReadData(PLCBase.eDataType.Type_32Bit, res.data[index], res.data[index + 1], hexType);
|
|
} else { // upper word 는 버퍼 범위를 벗어나므로, 없음
|
|
tempStrComp = _plc.Conv_ReadData(PLCBase.eDataType.Type_32Bit, res.data[index], 0, hexType);
|
|
}
|
|
if (tempStrComp != ioValue.Word) {
|
|
ioValue.Word = tempStrComp;
|
|
}
|
|
} else {
|
|
ioValue.Word = "";
|
|
ioValue.Use = false;
|
|
}
|
|
break;
|
|
case PLCBase.DisplayFormat.Bit32Even:
|
|
if (index % 2 == 0) {
|
|
ioValue.Use = true;
|
|
if (index + 1 < res.data.Count()) {
|
|
tempStrComp = _plc.Conv_ReadData(PLCBase.eDataType.Type_32Bit, res.data[index], res.data[index + 1], hexType);
|
|
} else { // upper word 는 버퍼 범위를 벗어나므로, 없음
|
|
tempStrComp = _plc.Conv_ReadData(PLCBase.eDataType.Type_32Bit, res.data[index], 0, hexType);
|
|
}
|
|
if (tempStrComp != ioValue.Word) {
|
|
ioValue.Word = tempStrComp;
|
|
}
|
|
} else {
|
|
ioValue.Use = false;
|
|
ioValue.Word = "";
|
|
}
|
|
break;
|
|
case PLCBase.DisplayFormat.ASCII: // LUKAS 수정
|
|
ioValue.Use = true;
|
|
tempStrComp = _plc.Conv_ReadData(PLCBase.eDataType.Type_Ascii, res.data[index], 0, false, true);
|
|
if (tempStrComp != ioValue.Word) {
|
|
ioValue.Word = tempStrComp;
|
|
}
|
|
break;
|
|
default: // 16 bit
|
|
ioValue.Use = true;
|
|
ioValue.Word = hexType ? res.data[index].ToString("X4") : res.data[index].ToString();
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
} catch (Exception ex) {
|
|
Log.E($"Exception: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void ChangeWordValue(object obj)
|
|
{
|
|
if (!_plc.IsActivated) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
// 사용자 권한에 따라서, 아예 해당 버튼을 disable 하는 방식으로 권한 검사를 처리함
|
|
// 방어용으로 이중 처리 (삭제 가능함)
|
|
var curPermissionLevel = ApplicationExt.PermissionLevel;
|
|
if (curPermissionLevel > (ushort)Aurora.Api.PermissionLevel.LEVEL3) { // 권한 부족
|
|
return;
|
|
}
|
|
|
|
// PLC 가 Auto 모드이면 Write 불가
|
|
if (PLC.CheckProcessAutoMode()) {
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// 비고. 리얼 모드에서는 입력의 수정 불가: 리얼모드 & Input 은 무시한다...
|
|
if (_plc.IsSimul == false && IsThisInput) {
|
|
return;
|
|
}
|
|
|
|
// 빈문자열도 체크해야 할까??
|
|
if (SelectedWordItem == null || WORD_DATA == null) {
|
|
return;
|
|
}
|
|
|
|
PLCBase.eSendResult eRet = PLCBase.eSendResult.OK;
|
|
int iSendSize = 1;
|
|
//string strSendData = WORD_DATA;
|
|
string[] strSendData = { WORD_DATA };
|
|
|
|
// display 옵션에 따른 구현
|
|
// 32bit odd, even 은 무조건 현재 선택된 주소가 low word 이고, 다음 주소가 high word 임.
|
|
string dataViewType = IsThisInput ? DataViewTypeInput : DataViewTypeOutput;
|
|
bool hexType = IsThisInput ? IsCheckedDataViewHexInput : IsCheckedDataViewHexOutput;
|
|
switch (dataViewType) {
|
|
case PLCBase.DisplayFormat.Bit16: {
|
|
if (_plc.IsSimul && IsThisInput) { // 시뮬 & 입력부분
|
|
eRet = _plc.WriteWordBlock(PLCBase.eDataType.Type_16Bit, SelectedWordItem.addrOffset, iSendSize, in strSendData, _deviceName, hexType, isOutput: false);
|
|
} else {
|
|
eRet = _plc.WriteWordBlock(PLCBase.eDataType.Type_16Bit, SelectedWordItem.addrOffset, iSendSize, in strSendData, _deviceName, hexType);
|
|
}
|
|
break;
|
|
}
|
|
case PLCBase.DisplayFormat.ASCII: {
|
|
// LUKAS: ABCDEFGH 를 입력할 경우, AB 가 1워드에 들어감, A 가 lower word임
|
|
|
|
// 예외1. 빈 문자열이면, 어떻게 하지..?? 일단은 선택된 메모리 주소만 널문자 주도록 하자 !!!
|
|
if (strSendData[0].Length == 0) { // 빈 문자열이라면,
|
|
strSendData[0] = "\0\0";
|
|
}
|
|
// write 할 워드 수는 문자열 길이의 절반... 1워드에 2개의 문자가 가능함
|
|
// 1 문자 --> 1 워드
|
|
// 2 문자 --> 1 워드
|
|
// 3 문자 --> 2 워드
|
|
// 4 문자 --> 2 워드
|
|
// ==> 2로 나눈 후, 올림처리
|
|
//iSendSize = (int)Math.Round(strSendData.Length / 2.0 + 0.5); // 오사오입 방식 반올림: 0.5 단위일 경우, 앞자리를 짝수로 만든다: 0.5 => 0, 1.5 = 2
|
|
iSendSize = (int)Math.Ceiling(strSendData[0].Length / 2.0);
|
|
|
|
// 예외 2. 보낼 워드 사이즈가 정의된 메모리 범위를 벗어날 경우, write 불가하고, 에러 로그를 남긴다.
|
|
if (_plc.IsSimul && IsThisInput) { // 시뮬 & 입력부분
|
|
if (DigitalInputs.Last().addrOffset < SelectedWordItem.addrOffset + iSendSize - 1) {
|
|
Log.E("ASCII write is impossible: Out of Word Input address range");
|
|
break;
|
|
}
|
|
eRet = _plc.WriteWordBlockForAscii(SelectedWordItem.addrOffset, iSendSize, ref strSendData, _deviceName, isOutput: false);
|
|
} else {
|
|
if (DigitalOutputs.Last().addrOffset < SelectedWordItem.addrOffset + iSendSize - 1) {
|
|
Log.E("ASCII write is impossible: Out of Word Output address range");
|
|
break;
|
|
}
|
|
eRet = _plc.WriteWordBlockForAscii(SelectedWordItem.addrOffset, iSendSize, ref strSendData, _deviceName);
|
|
}
|
|
break;
|
|
}
|
|
default: // 32 bit = 2 워드
|
|
// 검토사항: Odd/Even 과 총 메모리 주소의 홀짝 여부에 따라서, 마지막 주소가 선택될 수 있는데, 이 경우, 메모리 주소를 벗어날 수 있다.
|
|
// 선택된 주소가 메모리의 마지막 주소인 경우는 write 불가 (정의된 메모리 범위를 벗어나서 write 를 하는 행위이므로..)
|
|
if (_plc.IsSimul && IsThisInput) { // 시뮬 & 입력부분
|
|
if (DigitalInputs.Last().addrOffset == SelectedWordItem.addrOffset) {
|
|
Log.E("32bit write is impossible: upper word is out of address range");
|
|
break;
|
|
}
|
|
eRet = _plc.WriteWordBlock(PLCBase.eDataType.Type_32Bit, SelectedWordItem.addrOffset, iSendSize, in strSendData, _deviceName, hexType, isOutput: false);
|
|
} else { // 리얼모드
|
|
// 만약 선택된 주소가 마지막 주소라면, write 불가
|
|
if (DigitalOutputs.Last().addrOffset == SelectedWordItem.addrOffset) {
|
|
Log.E("32bit write is impossible: upper word is out of address range");
|
|
break;
|
|
}
|
|
eRet = _plc.WriteWordBlock(PLCBase.eDataType.Type_32Bit, SelectedWordItem.addrOffset, iSendSize, in strSendData, _deviceName, hexType);
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (eRet) {
|
|
case PLCBase.eSendResult.Error_NotActivated:
|
|
Log.E("PLC is not activated");
|
|
break;
|
|
case PLCBase.eSendResult.Error_NotConnect:
|
|
Log.E("PLC is not connected");
|
|
break;
|
|
case PLCBase.eSendResult.Error_Communication:
|
|
Log.E("PLC Comm Error");
|
|
break;
|
|
case PLCBase.eSendResult.Error_DataIsNumeric:
|
|
Log.E("Only numeric input is allowed");
|
|
break;
|
|
case PLCBase.eSendResult.Error_DataRange:
|
|
Log.E("Out of input range");
|
|
break;
|
|
case PLCBase.eSendResult.Error_DataLength:
|
|
Log.E("Out of input max. length");
|
|
break;
|
|
case PLCBase.eSendResult.Error_DataType:
|
|
Log.E("Input data type is invalid");
|
|
break;
|
|
case PLCBase.eSendResult.Error_Exception:
|
|
Log.E("Exception is occured");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|